summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore7
-rw-r--r--.gitmodules4
-rw-r--r--.ycm_extra_conf.py2
-rw-r--r--CMakeLists.txt28
-rw-r--r--INSTALL.md73
-rw-r--r--LICENSE.md2
-rw-r--r--Makefile78
-rw-r--r--README.md13
-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/fixtures/api/cache.dbbin1298432 -> 1298432 bytes
-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.cpp19
-rw-r--r--benchmark/util/tilecover.benchmark.cpp96
-rw-r--r--bin/offline.cpp82
-rw-r--r--bin/render.cpp115
-rw-r--r--circle.yml233
-rw-r--r--cmake/benchmark-files.cmake20
-rw-r--r--cmake/benchmark.cmake5
-rw-r--r--cmake/core-files.cmake164
-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.cmake23
-rw-r--r--cmake/mbgl.cmake127
-rw-r--r--cmake/node.cmake41
-rw-r--r--cmake/offline.cmake6
-rw-r--r--cmake/render.cmake7
-rw-r--r--cmake/test-files.cmake77
-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.hpp8
-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.hpp41
-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.hpp49
-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.hpp24
-rw-r--r--include/mbgl/style/conversion/source.hpp176
-rw-r--r--include/mbgl/style/conversion/tileset.hpp98
-rw-r--r--include/mbgl/style/conversion/transition_options.hpp32
-rw-r--r--include/mbgl/style/data_driven_property_value.hpp9
-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/length.hpp32
-rw-r--r--include/mbgl/style/expression/let.hpp83
-rw-r--r--include/mbgl/style/expression/literal.hpp56
-rw-r--r--include/mbgl/style/expression/match.hpp48
-rw-r--r--include/mbgl/style/expression/parsing_context.hpp172
-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/filter.hpp22
-rw-r--r--include/mbgl/style/filter_evaluator.hpp259
-rw-r--r--include/mbgl/style/function/camera_function.hpp64
-rw-r--r--include/mbgl/style/function/composite_function.hpp143
-rw-r--r--include/mbgl/style/function/convert.hpp356
-rw-r--r--include/mbgl/style/function/source_function.hpp50
-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/custom_layer.hpp89
-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/color.hpp2
-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.hpp16
-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)1177
-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.json6
-rw-r--r--platform/android/.gitignore4
-rw-r--r--platform/android/CHANGELOG.md146
-rw-r--r--platform/android/DISTRIBUTE.md2
-rw-r--r--platform/android/MapboxGLAndroidSDK/.gitignore3
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle71
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle21
-rw-r--r--platform/android/MapboxGLAndroidSDK/gradle.properties3
-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/proguard-rules.pro41
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java160
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java185
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java180
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java214
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java224
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java4
-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.java47
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java4
-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/camera/CameraUpdateFactory.java20
-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/constants/MapboxConstants.java57
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java50
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java42
-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.java222
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java3
-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.java21
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java1202
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java26
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java180
-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.java577
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java383
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java68
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java70
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java48
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java419
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java59
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java216
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java2
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java1104
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java389
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegion.java1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java3762
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java50
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java100
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java300
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/SourceFunction.java85
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/package-info.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops.java64
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops.java97
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IdentityStops.java18
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IntervalStops.java58
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IterableStops.java52
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stop.java109
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stops.java99
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/package-info.java6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java230
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java267
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java289
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java14
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java45
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java1360
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java41
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java25
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs32
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs72
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java8
-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.java16
-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/TileSet.java20
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java8
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java22
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java49
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-cs/strings.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-pt-rPT/strings.xml16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml17
-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/main/res/values/attrs.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java293
-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/maps/MapTouchListenersTest.java132
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java19
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java99
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java148
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java106
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java1168
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java102
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java34
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java5
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/build.gradle67
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/gradle-checkstyle.gradle19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java720
-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/action/WaitAction.java39
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseActivityTest.java37
-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/CameraInternalApiTest.java157
-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.java55
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java229
-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/storage/FileSourceTest.java129
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java236
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java1602
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java207
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java884
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java837
-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.java240
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java255
-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/LightTest.java114
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java1701
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java491
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java112
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java5017
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs415
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs60
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml384
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java104
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java300
-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.java131
-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/GestureDetectorActivity.java422
-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.java26
-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.java76
-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.java63
-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.java21
-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.java53
-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.java65
-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.java48
-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.java51
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java35
-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.java301
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java96
-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.java37
-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.java226
-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.java239
-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.java219
-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.java88
-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.java25
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java94
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java66
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java125
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java133
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java118
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java197
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java101
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java302
-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/activity/Feature.java10
-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/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpgbin0 -> 165806 bytes
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled.xml9
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml27
-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_gesture_detector.xml26
-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/layout/activity_my_location_customization.xml11
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml62
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml23
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml50
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml2
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml28
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_gesture_alert.xml14
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml4
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml32
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json43
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml13
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml12
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml1
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml11
-rw-r--r--platform/android/README.md86
-rw-r--r--platform/android/build.gradle17
-rw-r--r--platform/android/config.cmake79
-rw-r--r--platform/android/dependencies.gradle49
-rw-r--r--platform/android/gradle/dependencies.gradle58
-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-dependencies-graph.gradle29
-rw-r--r--platform/android/gradle/gradle-javadoc.gradle18
-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.json10
-rwxr-xr-x[-rw-r--r--]platform/android/scripts/generate-style-code.js22
-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/bitmap.cpp10
-rw-r--r--platform/android/src/bitmap.hpp1
-rw-r--r--platform/android/src/example_custom_layer.cpp76
-rw-r--r--platform/android/src/file_source.cpp10
-rw-r--r--platform/android/src/file_source.hpp2
-rw-r--r--platform/android/src/geojson/conversion/feature.hpp2
-rw-r--r--platform/android/src/geojson/conversion/geometry.hpp176
-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.cpp28
-rw-r--r--platform/android/src/geojson/feature_collection.hpp4
-rw-r--r--platform/android/src/geojson/geometry.cpp49
-rw-r--r--platform/android/src/geojson/geometry.hpp10
-rw-r--r--platform/android/src/geojson/geometry_collection.cpp63
-rw-r--r--platform/android/src/geojson/geometry_collection.hpp28
-rw-r--r--platform/android/src/geojson/line_string.cpp42
-rw-r--r--platform/android/src/geojson/line_string.hpp16
-rw-r--r--platform/android/src/geojson/multi_line_string.cpp33
-rw-r--r--platform/android/src/geojson/multi_line_string.hpp15
-rw-r--r--platform/android/src/geojson/multi_point.cpp20
-rw-r--r--platform/android/src/geojson/multi_point.hpp14
-rw-r--r--platform/android/src/geojson/multi_polygon.cpp39
-rw-r--r--platform/android/src/geojson/multi_polygon.hpp13
-rw-r--r--platform/android/src/geojson/point.cpp48
-rw-r--r--platform/android/src/geojson/point.hpp13
-rw-r--r--platform/android/src/geojson/polygon.cpp22
-rw-r--r--platform/android/src/geojson/polygon.hpp14
-rw-r--r--platform/android/src/geojson/position.cpp27
-rw-r--r--platform/android/src/geojson/position.hpp27
-rw-r--r--platform/android/src/geojson/util.hpp38
-rw-r--r--platform/android/src/geometry/lat_lng_bounds.cpp19
-rw-r--r--platform/android/src/gson/json_array.cpp19
-rw-r--r--platform/android/src/gson/json_array.hpp2
-rw-r--r--platform/android/src/gson/json_element.cpp28
-rw-r--r--platform/android/src/gson/json_element.hpp2
-rw-r--r--platform/android/src/gson/json_object.cpp17
-rw-r--r--platform/android/src/gson/json_object.hpp2
-rw-r--r--platform/android/src/gson/json_primitive.cpp80
-rw-r--r--platform/android/src/gson/json_primitive.hpp2
-rw-r--r--platform/android/src/java/lang.cpp76
-rw-r--r--platform/android/src/java/lang.hpp50
-rw-r--r--platform/android/src/java/util.cpp2
-rw-r--r--platform/android/src/java/util.hpp15
-rwxr-xr-xplatform/android/src/jni.cpp24
-rwxr-xr-xplatform/android/src/native_map_view.cpp53
-rwxr-xr-xplatform/android/src/native_map_view.hpp12
-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/function.hpp208
-rw-r--r--platform/android/src/style/conversion/geojson.hpp24
-rw-r--r--platform/android/src/style/conversion/property_value.hpp19
-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/functions/categorical_stops.cpp18
-rw-r--r--platform/android/src/style/functions/categorical_stops.hpp23
-rw-r--r--platform/android/src/style/functions/exponential_stops.cpp18
-rw-r--r--platform/android/src/style/functions/exponential_stops.hpp24
-rw-r--r--platform/android/src/style/functions/identity_stops.cpp18
-rw-r--r--platform/android/src/style/functions/identity_stops.hpp21
-rw-r--r--platform/android/src/style/functions/interval_stops.cpp18
-rw-r--r--platform/android/src/style/functions/interval_stops.hpp23
-rw-r--r--platform/android/src/style/functions/stop.cpp21
-rw-r--r--platform/android/src/style/functions/stop.hpp36
-rw-r--r--platform/android/src/style/layers/custom_layer.cpp10
-rw-r--r--platform/android/src/style/layers/custom_layer.hpp2
-rw-r--r--platform/android/src/style/layers/heatmap_layer.cpp145
-rw-r--r--platform/android/src/style/layers/heatmap_layer.hpp52
-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.cpp60
-rw-r--r--platform/android/src/style/layers/layer.cpp.ejs13
-rw-r--r--platform/android/src/style/layers/layer.hpp5
-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.hpp6
-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.ejs153
-rw-r--r--platform/darwin/docs/guides/Migrating to Expressions.md.ejs212
-rw-r--r--platform/darwin/docs/guides/Predicates and Expressions.md889
-rw-r--r--platform/darwin/docs/guides/Tile URL Templates.md.ejs14
-rw-r--r--platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs99
-rw-r--r--platform/darwin/docs/img/data-driven-styling/cast.pngbin0 -> 49974 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/categorical1.pngbin105968 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/categorical2.pngbin125698 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/citibikes.pngbin152843 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential-function-1.pngbin69991 -> 34835 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential-function.pngbin70066 -> 34933 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/exponential.pngbin86910 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/identity.pngbin68837 -> 70278 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/interval.pngbin83927 -> 0 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/multiply.pngbin0 -> 50375 bytes
-rw-r--r--platform/darwin/docs/img/data-driven-styling/polylineExample.pngbin156800 -> 0 bytes
-rw-r--r--platform/darwin/docs/theme/assets/css/jazzy.css.scss7
-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/da.lproj/Foundation.strings297
-rw-r--r--platform/darwin/resources/da.lproj/Foundation.stringsdict54
-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/pt-PT.lproj/Foundation.stringsdict54
-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.js232
-rw-r--r--platform/darwin/scripts/style-spec-overrides-v8.json31
-rwxr-xr-x[-rw-r--r--]platform/darwin/scripts/update-examples.js12
-rw-r--r--platform/darwin/src/MGLAccountManager.h26
-rw-r--r--platform/darwin/src/MGLAccountManager.m11
-rw-r--r--platform/darwin/src/MGLAccountManager_Private.h2
-rw-r--r--platform/darwin/src/MGLAttributionInfo.mm14
-rw-r--r--platform/darwin/src/MGLAttributionInfo_Private.h6
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.h74
-rw-r--r--platform/darwin/src/MGLBackgroundStyleLayer.mm31
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.h408
-rw-r--r--platform/darwin/src/MGLCircleStyleLayer.mm122
-rw-r--r--platform/darwin/src/MGLCompassDirectionFormatter.m4
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.h165
-rw-r--r--platform/darwin/src/MGLComputedShapeSource.mm225
-rw-r--r--platform/darwin/src/MGLComputedShapeSource_Private.h12
-rw-r--r--platform/darwin/src/MGLConversion.h227
-rw-r--r--platform/darwin/src/MGLFeature.h35
-rw-r--r--platform/darwin/src/MGLFeature.mm14
-rw-r--r--platform/darwin/src/MGLFeature_Private.h4
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.h234
-rw-r--r--platform/darwin/src/MGLFillExtrusionStyleLayer.mm79
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.h268
-rw-r--r--platform/darwin/src/MGLFillStyleLayer.mm83
-rw-r--r--platform/darwin/src/MGLForegroundStyleLayer.h9
-rw-r--r--platform/darwin/src/MGLGeometry.h32
-rw-r--r--platform/darwin/src/MGLGeometry.mm16
-rw-r--r--platform/darwin/src/MGLGeometry_Private.h2
-rw-r--r--platform/darwin/src/MGLHeatmapStyleLayer.h215
-rw-r--r--platform/darwin/src/MGLHeatmapStyleLayer.mm210
-rw-r--r--platform/darwin/src/MGLHillshadeStyleLayer.h324
-rw-r--r--platform/darwin/src/MGLHillshadeStyleLayer.mm239
-rw-r--r--platform/darwin/src/MGLLight.h101
-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.h461
-rw-r--r--platform/darwin/src/MGLLineStyleLayer.mm153
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.h2
-rw-r--r--platform/darwin/src/MGLMapSnapshotter.mm3
-rw-r--r--platform/darwin/src/MGLMultiPoint.mm2
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.h8
-rw-r--r--platform/darwin/src/MGLNetworkConfiguration.m5
-rw-r--r--platform/darwin/src/MGLOfflineStorage.h12
-rw-r--r--platform/darwin/src/MGLOfflineStorage.mm12
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.h3
-rw-r--r--platform/darwin/src/MGLOpenGLStyleLayer.mm92
-rw-r--r--platform/darwin/src/MGLPointAnnotation.h5
-rw-r--r--platform/darwin/src/MGLPointCollection.h5
-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.h17
-rw-r--r--platform/darwin/src/MGLPolygon.mm32
-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.h13
-rw-r--r--platform/darwin/src/MGLPolyline.mm27
-rw-r--r--platform/darwin/src/MGLPolyline_Private.h12
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.h50
-rw-r--r--platform/darwin/src/MGLRasterDEMSource.mm17
-rw-r--r--platform/darwin/src/MGLRasterSource_Private.h8
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.h178
-rw-r--r--platform/darwin/src/MGLRasterStyleLayer.mm100
-rw-r--r--platform/darwin/src/MGLRasterTileSource.h (renamed from platform/darwin/src/MGLRasterSource.h)47
-rw-r--r--platform/darwin/src/MGLRasterTileSource.mm (renamed from platform/darwin/src/MGLRasterSource.mm)27
-rw-r--r--platform/darwin/src/MGLRasterTileSource_Private.h21
-rw-r--r--platform/darwin/src/MGLRendererConfiguration.h4
-rw-r--r--platform/darwin/src/MGLRendererFrontend.h4
-rw-r--r--platform/darwin/src/MGLShape.h18
-rw-r--r--platform/darwin/src/MGLShapeCollection.h9
-rw-r--r--platform/darwin/src/MGLShapeCollection.mm4
-rw-r--r--platform/darwin/src/MGLShapeSource.h92
-rw-r--r--platform/darwin/src/MGLShapeSource.mm133
-rw-r--r--platform/darwin/src/MGLShapeSource_Private.h5
-rw-r--r--platform/darwin/src/MGLSource.h9
-rw-r--r--platform/darwin/src/MGLStyle.h113
-rw-r--r--platform/darwin/src/MGLStyle.mm263
-rw-r--r--platform/darwin/src/MGLStyleLayer.h.ejs13
-rw-r--r--platform/darwin/src/MGLStyleLayer.mm.ejs80
-rw-r--r--platform/darwin/src/MGLStyleValue.h470
-rw-r--r--platform/darwin/src/MGLStyleValue.mm334
-rw-r--r--platform/darwin/src/MGLStyleValue_Private.h640
-rw-r--r--platform/darwin/src/MGLStyle_Private.h12
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h1665
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm586
-rw-r--r--platform/darwin/src/MGLTileSource.h38
-rw-r--r--platform/darwin/src/MGLTileSource.mm25
-rw-r--r--platform/darwin/src/MGLTileSource_Private.h4
-rw-r--r--platform/darwin/src/MGLTypes.h20
-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.mm73
-rw-r--r--platform/darwin/src/MGLVectorSource_Private.h8
-rw-r--r--platform/darwin/src/MGLVectorStyleLayer.h117
-rw-r--r--platform/darwin/src/MGLVectorTileSource.h (renamed from platform/darwin/src/MGLVectorSource.h)45
-rw-r--r--platform/darwin/src/MGLVectorTileSource.mm153
-rw-r--r--platform/darwin/src/MGLVectorTileSource_Private.h18
-rw-r--r--platform/darwin/src/NSArray+MGLAdditions.mm2
-rw-r--r--platform/darwin/src/NSBundle+MGLAdditions.h2
-rw-r--r--platform/darwin/src/NSBundle+MGLAdditions.m2
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm298
-rw-r--r--platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm62
-rw-r--r--platform/darwin/src/NSDictionary+MGLAdditions.mm2
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h197
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm1317
-rw-r--r--platform/darwin/src/NSExpression+MGLPrivateAdditions.h89
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.h14
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm192
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.h11
-rw-r--r--platform/darwin/src/NSString+MGLAdditions.m26
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.h13
-rw-r--r--platform/darwin/src/NSValue+MGLAdditions.m10
-rw-r--r--platform/darwin/src/headless_backend_cgl.cpp132
-rw-r--r--platform/darwin/src/headless_backend_eagl.mm52
-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.m10
-rw-r--r--platform/darwin/test/MGLBackgroundStyleLayerTests.mm129
-rw-r--r--platform/darwin/test/MGLCircleStyleLayerTests.mm568
-rw-r--r--platform/darwin/test/MGLComputedShapeSourceTests.m24
-rw-r--r--platform/darwin/test/MGLDocumentationExampleTests.swift157
-rw-r--r--platform/darwin/test/MGLDocumentationGuideTests.swift242
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm955
-rw-r--r--platform/darwin/test/MGLFeatureTests.mm33
-rw-r--r--platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm344
-rw-r--r--platform/darwin/test/MGLFillStyleLayerTests.mm344
-rw-r--r--platform/darwin/test/MGLGeometryTests.mm9
-rw-r--r--platform/darwin/test/MGLHeatmapColorTests.mm62
-rw-r--r--platform/darwin/test/MGLHeatmapStyleLayerTests.mm300
-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.mm675
-rw-r--r--platform/darwin/test/MGLNSStringAdditionsTests.m26
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm696
-rw-r--r--platform/darwin/test/MGLRasterStyleLayerTests.mm310
-rw-r--r--platform/darwin/test/MGLSDKTestHelpers.swift18
-rw-r--r--platform/darwin/test/MGLShapeSourceTests.mm3
-rw-r--r--platform/darwin/test/MGLSourceQueryTests.m4
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.h5
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.m4
-rw-r--r--platform/darwin/test/MGLStyleLayerTests.mm.ejs73
-rw-r--r--platform/darwin/test/MGLStyleTests.mm154
-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.mm2332
-rw-r--r--platform/darwin/test/MGLTileSetTests.mm29
-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.cpp8
-rw-r--r--platform/default/mbgl/storage/offline_database.cpp508
-rw-r--r--platform/default/mbgl/storage/offline_database.hpp20
-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/mbgl/util/default_styles.hpp3
-rw-r--r--platform/default/run_loop.cpp7
-rw-r--r--platform/default/sqlite3.cpp333
-rw-r--r--platform/default/sqlite3.hpp101
-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.md10
-rw-r--r--platform/ios/DEVELOPING.md16
-rw-r--r--platform/ios/INSTALL.md39
-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.m202
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.h20
-rw-r--r--platform/ios/Integration Tests/MGLMapViewIntegrationTest.m79
-rw-r--r--platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec4
-rw-r--r--platform/ios/Mapbox-iOS-SDK-static-part.podspec2
-rw-r--r--platform/ios/Mapbox-iOS-SDK-symbols.podspec4
-rw-r--r--platform/ios/Mapbox-iOS-SDK.podspec4
-rw-r--r--platform/ios/Mapbox.playground/Contents.swift117
-rw-r--r--platform/ios/Mapbox.playground/timeline.xctimeline6
-rw-r--r--platform/ios/README.md3
-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/MBXCustomCalloutView.m4
-rw-r--r--platform/ios/app/MBXOfflinePacksTableViewController.m6
-rw-r--r--platform/ios/app/MBXViewController.m429
-rw-r--r--platform/ios/app/Main.storyboard60
-rw-r--r--platform/ios/app/da.lproj/Localizable.strings0
-rw-r--r--platform/ios/app/he.lproj/Localizable.strings0
-rw-r--r--platform/ios/app/pt-PT.lproj/Localizable.strings0
-rw-r--r--platform/ios/benchmark/Info.plist2
-rw-r--r--platform/ios/benchmark/MBXBenchViewController.mm4
-rw-r--r--platform/ios/config.cmake17
-rw-r--r--platform/ios/docs/doc-README.md2
-rw-r--r--platform/ios/docs/guides/Adding Markers to a Map.md62
-rw-r--r--platform/ios/docs/guides/Adding Points to a Map.md82
-rw-r--r--platform/ios/docs/guides/For Style Authors.md150
-rw-r--r--platform/ios/docs/guides/Info.plist Keys.md2
-rw-r--r--platform/ios/docs/guides/Migrating to Expressions.md266
-rw-r--r--platform/ios/docs/guides/Tile URL Templates.md14
-rw-r--r--platform/ios/docs/guides/Using Style Functions at Runtime.md162
-rw-r--r--platform/ios/docs/img/adding-points-to-a-map/annotation-image.pngbin0 -> 171537 bytes
-rw-r--r--platform/ios/docs/img/adding-points-to-a-map/annotation-view.pngbin0 -> 79919 bytes
-rw-r--r--platform/ios/docs/img/adding-points-to-a-map/circle-layer.pngbin0 -> 215965 bytes
-rw-r--r--platform/ios/docs/img/adding-points-to-a-map/symbol-layer.pngbin0 -> 75743 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/CustomAnnotations.gifbin45604 -> 44108 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/DynamicStyles.gifbin97235 -> 94908 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/Emoji.gifbin177077 -> 126330 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/HexBins.gifbin554029 -> 435797 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/Population.gifbin247152 -> 228484 bytes
-rw-r--r--platform/ios/docs/img/runtime-styling/SnowLevels.gifbin489450 -> 463142 bytes
-rw-r--r--platform/ios/docs/img/studio-workflow/add-properties.gifbin239499 -> 176869 bytes
-rw-r--r--platform/ios/docs/img/studio-workflow/create-polygons.gifbin1659146 -> 565786 bytes
-rw-r--r--platform/ios/docs/img/studio-workflow/property-values.pngbin83518 -> 39756 bytes
-rw-r--r--platform/ios/docs/img/studio-workflow/stop-functions.pngbin166947 -> 92170 bytes
-rw-r--r--platform/ios/docs/pod-README.md4
-rw-r--r--platform/ios/framework/Settings.bundle/ca.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/da.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/de.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/es.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/fi.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/fr.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/he.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/lt.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/nl.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/pl.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/pt-PT.lproj/Root.strings3
-rw-r--r--platform/ios/framework/Settings.bundle/ru.lproj/Root.strings6
-rw-r--r--platform/ios/framework/Settings.bundle/sv.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/uk.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/vi.lproj/Root.strings2
-rw-r--r--platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings2
-rw-r--r--platform/ios/ios.xcodeproj/project.pbxproj1127
-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.yml18
-rw-r--r--platform/ios/resources/api_mapbox_cn-digicert_2018.derbin1704 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_cn-geotrust_2018.derbin1578 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-digicert_2016.derbin1913 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-digicert_2017.derbin2030 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-geotrust_2016.derbin1757 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_com-geotrust_2017.derbin1758 -> 0 bytes
-rw-r--r--platform/ios/resources/api_mapbox_staging.derbin1334 -> 0 bytes
-rw-r--r--platform/ios/resources/da.lproj/Localizable.strings117
-rw-r--r--platform/ios/resources/da.lproj/Localizable.stringsdict54
-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/pt-PT.lproj/Localizable.stringsbin0 -> 8276 bytes
-rw-r--r--platform/ios/resources/pt-PT.lproj/Localizable.stringsdict54
-rw-r--r--platform/ios/resources/ru.lproj/Localizable.strings58
-rw-r--r--platform/ios/resources/ru.lproj/Localizable.stringsdict50
-rw-r--r--platform/ios/resources/sv.lproj/Localizable.strings58
-rw-r--r--platform/ios/resources/sv.lproj/Localizable.stringsdict38
-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/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift2
-rw-r--r--platform/ios/src/MGLAPIClient.h15
-rw-r--r--platform/ios/src/MGLAPIClient.m218
-rw-r--r--platform/ios/src/MGLAnnotationContainerView.h2
-rw-r--r--platform/ios/src/MGLAnnotationContainerView.m4
-rw-r--r--platform/ios/src/MGLAnnotationContainerView_Private.h2
-rw-r--r--platform/ios/src/MGLAnnotationView.h8
-rw-r--r--platform/ios/src/MGLAnnotationView.mm15
-rw-r--r--platform/ios/src/MGLCalloutView.h27
-rw-r--r--platform/ios/src/MGLLocationManager.h25
-rw-r--r--platform/ios/src/MGLLocationManager.m175
-rw-r--r--platform/ios/src/MGLMapAccessibilityElement.mm31
-rw-r--r--platform/ios/src/MGLMapView+IBAdditions.h1
-rw-r--r--platform/ios/src/MGLMapView.h165
-rw-r--r--platform/ios/src/MGLMapView.mm545
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h2
-rw-r--r--platform/ios/src/MGLMapboxEvents.h39
-rw-r--r--platform/ios/src/MGLMapboxEvents.m831
-rw-r--r--platform/ios/src/MGLScaleBar.mm7
-rw-r--r--platform/ios/src/MGLTelemetryConfig.h2
-rw-r--r--platform/ios/src/MGLUserLocation.m2
-rw-r--r--platform/ios/src/Mapbox-Prefix.pch1
-rw-r--r--platform/ios/src/Mapbox.h9
-rw-r--r--platform/ios/src/NSOrthography+MGLAdditions.h18
-rw-r--r--platform/ios/src/NSOrthography+MGLAdditions.m31
-rw-r--r--platform/ios/src/UIColor+MGLAdditions.h7
-rw-r--r--platform/ios/src/UIColor+MGLAdditions.mm49
-rw-r--r--platform/ios/src/UIImage+MGLAdditions.mm4
-rw-r--r--platform/ios/test/MGLAnnotationViewTests.m63
-rw-r--r--platform/ios/test/MGLMapAccessibilityElementTests.m2
-rw-r--r--platform/ios/test/MGLMapViewLayoutTests.m7
-rw-r--r--platform/ios/test/MGLMapViewScaleBarTests.m62
-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
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.h12
-rwxr-xr-xplatform/ios/vendor/SMCalloutView/SMCalloutView.m43
m---------platform/ios/vendor/mapbox-events-ios0
-rw-r--r--platform/linux/README.md20
-rw-r--r--platform/linux/config.cmake41
-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.md70
-rw-r--r--platform/macos/DEVELOPING.md18
-rw-r--r--platform/macos/INSTALL.md36
-rw-r--r--platform/macos/Mapbox-macOS-SDK-symbols.podspec4
-rw-r--r--platform/macos/Mapbox-macOS-SDK.podspec4
-rw-r--r--platform/macos/app/AppDelegate.m14
-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.xib70
-rw-r--r--platform/macos/app/Base.lproj/MapDocument.xib17
-rw-r--r--platform/macos/app/MGLStyle+MBXAdditions.h2
-rw-r--r--platform/macos/app/MGLStyle+MBXAdditions.m6
-rw-r--r--platform/macos/app/MapDocument.m276
-rw-r--r--platform/macos/app/StyleLayerIconTransformer.m8
-rw-r--r--platform/macos/app/da.lproj/Localizable.strings0
-rw-r--r--platform/macos/app/he.lproj/Localizable.strings0
-rw-r--r--platform/macos/app/heatmap.json809
-rw-r--r--platform/macos/app/pt-PT.lproj/Localizable.strings0
-rw-r--r--platform/macos/app/resources/heatmap.svg72
-rw-r--r--platform/macos/app/resources/hillshade.svg87
-rw-r--r--platform/macos/config.cmake34
-rw-r--r--platform/macos/docs/doc-README.md2
-rw-r--r--platform/macos/docs/guides/For Style Authors.md150
-rw-r--r--platform/macos/docs/guides/Info.plist Keys.md2
-rw-r--r--platform/macos/docs/guides/Migrating to Expressions.md267
-rw-r--r--platform/macos/docs/guides/Tile URL Templates.md14
-rw-r--r--platform/macos/docs/guides/Using Style Functions at Runtime.md162
-rw-r--r--platform/macos/docs/img/screenshot.jpgbin351938 -> 411181 bytes
-rw-r--r--platform/macos/docs/pod-README.md4
-rw-r--r--platform/macos/jazzy.yml17
-rw-r--r--platform/macos/macos.xcodeproj/project.pbxproj214
-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.xcscheme3
-rw-r--r--platform/macos/sdk/Info.plist6
-rw-r--r--platform/macos/sdk/ar.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/da.lproj/Localizable.strings27
-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/hu.lproj/Localizable.strings4
-rw-r--r--platform/macos/sdk/pt-PT.lproj/Localizable.strings27
-rw-r--r--platform/macos/sdk/ru.lproj/Localizable.strings6
-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+IBAdditions.mm14
-rw-r--r--platform/macos/src/MGLMapView.h86
-rw-r--r--platform/macos/src/MGLMapView.mm242
-rw-r--r--platform/macos/src/Mapbox.h9
-rw-r--r--platform/macos/src/NSColor+MGLAdditions.h9
-rw-r--r--platform/macos/src/NSColor+MGLAdditions.mm90
-rw-r--r--platform/macos/src/NSImage+MGLAdditions.mm4
-rw-r--r--platform/node/CHANGELOG.md11
-rw-r--r--platform/node/DEVELOPING.md10
-rw-r--r--platform/node/README.md3
-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.cpp199
-rw-r--r--platform/node/src/node_map.hpp6
-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.json115
-rw-r--r--platform/node/test/js/map.test.js5
-rw-r--r--platform/node/test/suite_implementation.js25
-rw-r--r--platform/qt/README.md78
-rw-r--r--platform/qt/app/mapwindow.cpp60
-rw-r--r--platform/qt/app/mapwindow.hpp1
-rw-r--r--platform/qt/config.cmake55
-rw-r--r--platform/qt/config.qdocconf4
-rw-r--r--platform/qt/include/QQuickMapboxGL1
-rw-r--r--platform/qt/include/QQuickMapboxGLMapParameter1
-rw-r--r--platform/qt/include/qmapbox.hpp40
-rw-r--r--platform/qt/include/qmapboxgl.hpp50
-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.cpp42
-rw-r--r--platform/qt/src/qmapboxgl.cpp527
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.cpp120
-rw-r--r--platform/qt/src/qmapboxgl_map_observer.hpp46
-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.cpp274
-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.js5
-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.js4
-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.cpp115
-rw-r--r--src/mbgl/geometry/feature_index.hpp62
-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.cpp37
-rw-r--r--src/mbgl/layout/symbol_instance.hpp16
-rw-r--r--src/mbgl/layout/symbol_layout.cpp300
-rw-r--r--src/mbgl/layout/symbol_layout.hpp15
-rw-r--r--src/mbgl/layout/symbol_projection.cpp160
-rw-r--r--src/mbgl/layout/symbol_projection.hpp42
-rw-r--r--src/mbgl/map/map.cpp41
-rw-r--r--src/mbgl/map/transform.cpp26
-rw-r--r--src/mbgl/map/transform.hpp8
-rw-r--r--src/mbgl/map/transform_state.cpp43
-rw-r--r--src/mbgl/map/transform_state.hpp6
-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.hpp89
-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.cpp173
-rw-r--r--src/mbgl/renderer/buckets/symbol_bucket.hpp64
-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.cpp28
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.cpp73
-rw-r--r--src/mbgl/renderer/layers/render_circle_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.cpp33
-rw-r--r--src/mbgl/renderer/layers/render_custom_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_fill_layer.hpp3
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp178
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.hpp49
-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_line_layer.cpp7
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.hpp3
-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.hpp16
-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.cpp207
-rw-r--r--src/mbgl/renderer/renderer_impl.hpp24
-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.cpp66
-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.cpp70
-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/line.cpp3
-rw-r--r--src/mbgl/shaders/line_pattern.cpp10
-rw-r--r--src/mbgl/shaders/preludes.cpp4
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp31
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp61
-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.cpp304
-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/stringify.hpp135
-rw-r--r--src/mbgl/style/conversion/tileset.cpp14
-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.cpp70
-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.cpp554
-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.cpp249
-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/length.cpp66
-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.cpp256
-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.cpp354
-rw-r--r--src/mbgl/style/filter.cpp13
-rw-r--r--src/mbgl/style/filter_evaluator.cpp225
-rw-r--r--src/mbgl/style/function/expression.cpp38
-rw-r--r--src/mbgl/style/layers/background_layer.cpp2
-rw-r--r--src/mbgl/style/layers/circle_layer.cpp2
-rw-r--r--src/mbgl/style/layers/custom_layer.cpp16
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.cpp12
-rw-r--r--src/mbgl/style/layers/custom_layer_impl.hpp14
-rw-r--r--src/mbgl/style/layers/fill_extrusion_layer.cpp2
-rw-r--r--src/mbgl/style/layers/fill_layer.cpp2
-rw-r--r--src/mbgl/style/layers/heatmap_layer.cpp241
-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.cpp240
-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.ejs16
-rw-r--r--src/mbgl/style/layers/layer_properties.hpp.ejs1
-rw-r--r--src/mbgl/style/layers/line_layer.cpp2
-rw-r--r--src/mbgl/style/layers/raster_layer.cpp2
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp8
-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.cpp339
-rw-r--r--src/mbgl/text/collision_index.hpp69
-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.cpp193
-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.cpp372
-rw-r--r--src/mbgl/text/placement.hpp108
-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/geojson_tile.cpp2
-rw-r--r--src/mbgl/tile/geometry_tile.cpp153
-rw-r--r--src/mbgl/tile/geometry_tile.hpp74
-rw-r--r--src/mbgl/tile/geometry_tile_worker.cpp185
-rw-r--r--src/mbgl/tile/geometry_tile_worker.hpp26
-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.cpp10
-rw-r--r--src/mbgl/tile/tile.hpp30
-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.cpp22
-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/i18n.cpp37
-rw-r--r--src/mbgl/util/i18n.hpp2
-rw-r--r--src/mbgl/util/intersection_tests.cpp9
-rw-r--r--src/mbgl/util/intersection_tests.hpp1
-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/throttler.cpp36
-rw-r--r--src/mbgl/util/throttler.hpp22
-rw-r--r--src/mbgl/util/tile_cover.cpp108
-rw-r--r--src/mbgl/util/tile_cover.hpp26
-rw-r--r--src/mbgl/util/tile_cover_impl.cpp365
-rw-r--r--src/mbgl/util/tile_cover_impl.hpp90
-rw-r--r--src/mbgl/util/tiny_sdf.cpp2
-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.cpp40
-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/map/prefetch/expected.pngbin2198 -> 0 bytes
-rw-r--r--test/fixtures/map/prefetch/tile.png (renamed from test/fixtures/map/prefetch/tile_green.png)bin659 -> 659 bytes
-rw-r--r--test/fixtures/map/prefetch/tile_red.pngbin659 -> 0 bytes
-rw-r--r--test/fixtures/offline_database/satellite_test.dbbin0 -> 114688 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.cpp28
-rw-r--r--test/gl/context.test.cpp114
-rw-r--r--test/map/map.test.cpp50
-rw-r--r--test/map/prefetch.test.cpp40
-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/src/mbgl/test/stub_map_observer.hpp49
-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.cpp123
-rw-r--r--test/storage/sqlite.test.cpp32
-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/conversion/stringify.test.cpp27
-rw-r--r--test/style/expression/expression.test.cpp95
-rw-r--r--test/style/expression/util.test.cpp23
-rw-r--r--test/style/filter.test.cpp155
-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.cpp207
-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.cpp34
-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/tile_cover.test.cpp232
-rw-r--r--test/util/tile_range.test.cpp8
-rw-r--r--test/util/unique_any.test.cpp186
1449 files changed, 64093 insertions, 44443 deletions
diff --git a/.gitignore b/.gitignore
index ce65e4c9b6..b32baaac09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,8 @@ xcuserdata
/test/fixtures/api/2.png
/test/fixtures/offline_database/offline.db
/test/fixtures/offline_database/offline.db-*
+/test/fixtures/offline_database/satellite.db
+/test/fixtures/offline_database/satellite.db-*
/test/fixtures/offline_database/invalid.db
/test/fixtures/offline_database/invalid.db-*
/test/fixtures/offline_database/migrated.db
@@ -34,3 +36,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
diff --git a/.gitmodules b/.gitmodules
index 422fc3930e..e5be61a921 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -7,3 +7,7 @@
[submodule "mapbox-gl-js"]
path = mapbox-gl-js
url = https://github.com/mapbox/mapbox-gl-js.git
+[submodule "platform/ios/vendor/mapbox-events-ios"]
+ path = platform/ios/vendor/mapbox-events-ios
+ url = https://github.com/mapbox/mapbox-events-ios.git
+ branch = boundsj-add-namespace-header
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..46aea73356 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,14 +2,16 @@ 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)
+option(WITH_ERROR "Add -Werror flag to build (turns warnings into errors)" ON)
+
+include(cmake/mbgl.cmake)
+include(cmake/mason.cmake)
+include(cmake/xcode.cmake)
if(WITH_CXX11ABI)
set(MASON_CXXABI_SUFFIX -cxx11abi)
@@ -44,10 +46,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 +71,21 @@ 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 -Wno-variadic-macros -Wno-unknown-pragmas")
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -fvisibility=hidden -Wall -Wextra -Wshadow -Wno-variadic-macros -Wno-unknown-pragmas")
+
+if (WITH_ERROR)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror")
+endif()
+
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..463d199d10 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -1,68 +1,15 @@
-# Building & Developing Mapbox GL Native from Source
+# Building & Developing from Source
-**Just trying to use Mapbox GL Native? You don't need to read this stuff! We
+**Just trying to use one of the Mapbox Maps SDKs? You don't need to read this stuff! We
provide [easy-to-install, prebuilt versions of the Mapbox Maps SDKs for iOS and Android
that you can download instantly and get started with fast](https://www.mapbox.com/install/).**
-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.
+If you're certain you need to build from source, rather than using a prebuilt SDK, please
+refer to the installation instructions for the platform of interest:
-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.
-
-## 1: Getting the source
-
-Clone the git repository:
-
- git clone https://github.com/mapbox/mapbox-gl-native.git
- cd mapbox-gl-native
-
-## 2: Installing dependencies
-
-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
-
- - [`libcurl`](http://curl.haxx.se/libcurl/) (depends on OpenSSL)
-
-### 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`)
-
-### Optional dependencies
-
-- [ccache](https://ccache.samba.org) (for build only; improves recompilation performance)
-
-## 3: Setting up a development environment & building
-
-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/)
-* [Mapbox Qt SDK](platform/qt/)
-* [Mapbox GL Native on Linux](platform/linux/)
-* [node-mapbox-gl-native](platform/node/)
-
-## 4: Keeping up to date
-
-This repository uses Git submodules, which should be automatically checked out when you first run a `make` command for one of the above platforms. These submodules are not updated automatically and we recommended that you run `git submodule update` after pulling down new commits to this repository.
+* [Mapbox Maps SDK for Android](platform/android/README.md)
+* [Mapbox Maps SDK for iOS](platform/ios/INSTALL.md)
+* [Mapbox Maps SDK for macOS](platform/macos/INSTALL.md)
+* [Mapbox Maps SDK for Qt](platform/qt/README.md)
+* [Mapbox GL Native on Linux](platform/linux/README.md)
+* [node-mapbox-gl-native](platform/node/DEVELOPING.md)
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..576312cabf 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,7 @@ ifeq ($(V), 1)
export XCPRETTY
NINJA_ARGS ?= -v
else
- export XCPRETTY ?= | xcpretty
+ export XCPRETTY ?= | tee '$(shell pwd)/build/xcodebuild-$(shell date +"%Y-%m-%d_%H%M%S").log' | xcpretty
NINJA_ARGS ?=
endif
@@ -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,17 +222,30 @@ 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
+ios-sanitize: $(IOS_PROJ_PATH)
+ set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableThreadSanitizer YES -enableUndefinedBehaviorSanitizer YES test $(XCPRETTY)
+
.PHONY: ios-sanitize-address
ios-sanitize-address: $(IOS_PROJ_PATH)
set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableAddressSanitizer YES test $(XCPRETTY)
-.PHONY: ios-sanitize-thread
-ios-sanitize-thread: $(IOS_PROJ_PATH)
- set -o pipefail && $(IOS_XCODEBUILD_SIM) -scheme 'CI' -enableThreadSanitizer YES test $(XCPRETTY)
+.PHONY: ios-static-analyzer
+ios-static-analyzer: $(IOS_PROJ_PATH)
+ set -o pipefail && $(IOS_XCODEBUILD_SIM) analyze -scheme 'CI' test $(XCPRETTY)
.PHONY: ipackage
ipackage: $(IOS_PROJ_PATH)
@@ -296,7 +313,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 +498,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 +506,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 +558,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 +611,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 +653,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 +678,19 @@ 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
+
+# Creates a dependency graph using Graphviz
+.PHONY: android-graph
+android-graph:
+ cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:generateDependencyGraphMapboxLibraries
#### Miscellaneous targets #####################################################
@@ -684,7 +706,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..0d6e731103 100644
--- a/README.md
+++ b/README.md
@@ -10,21 +10,24 @@ This repository hosts the cross-platform Mapbox GL Native library, plus convenie
| --------------------------------------- | ---------------------------------- | ---------------------------------------- |
| [Mapbox GL Native](INSTALL.md) | C++14 | [![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) [![Coverage Status](https://coveralls.io/repos/github/mapbox/mapbox-gl-native/badge.svg?branch=master)](https://coveralls.io/github/mapbox/mapbox-gl-native?branch=master) |
| [Mapbox Maps SDK for Android](platform/android/) | Java | [![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 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) |
+| [Mapbox Maps SDK for iOS](platform/ios/) | Objective-C or Swift | [![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 Maps SDK for macOS](platform/macos/) | Objective-C, Swift, or AppleScript | [![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) |
| [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 Maps SDK for Qt](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:
| Toolkit | Android | iOS | Developer |
| ---------------------------------------- | --------|-----|------------ |
-| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox |
+| [React Native](https://github.com/mapbox/react-native-mapbox-gl/) ([npm](https://www.npmjs.com/package/@mapbox/react-native-mapbox-gl)) | :white_check_mark: | :white_check_mark: | Mapbox |
| [Apache Cordova](http://plugins.telerik.com/cordova/plugin/mapbox/) ([npm](https://www.npmjs.com/package/cordova-plugin-mapbox)) | :white_check_mark: | :white_check_mark: | Telerik |
-| [NativeScript](http://plugins.telerik.com/nativescript/plugin/mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik |
+| [NativeScript](https://market.nativescript.org/plugins/nativescript-mapbox/) ([npm](https://www.npmjs.com/package/nativescript-mapbox/)) | :white_check_mark: | :white_check_mark: | Telerik |
| [Xamarin](https://components.xamarin.com/view/mapboxsdk/) | :white_check_mark: | :white_check_mark: | Xamarin |
If your platform or hybrid application framework isn’t listed here, consider embedding [Mapbox GL JS](https://github.com/mapbox/mapbox-gl-js) using the standard Web capabilities on your platform.
## License
+
+Mapbox GL Native is licensed under the [3-Clause BSD license](LICENSE.md). The licenses of its dependencies are tracked via [FOSSA](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native):
+
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmapbox%2Fmapbox-gl-native)
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/fixtures/api/cache.db b/benchmark/fixtures/api/cache.db
index 6a1d60421f..10452b2714 100644
--- a/benchmark/fixtures/api/cache.db
+++ b/benchmark/fixtures/api/cache.db
Binary files differ
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..e4cf635256 100644
--- a/benchmark/parse/filter.benchmark.cpp
+++ b/benchmark/parse/filter.benchmark.cpp
@@ -2,20 +2,17 @@
#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>
+#include <mbgl/benchmark/stub_geometry_tile_feature.hpp>
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) {
@@ -26,15 +23,11 @@ static void Parse_Filter(benchmark::State& state) {
static void Parse_EvaluateFilter(benchmark::State& state) {
const style::Filter filter = parse(R"FILTER(["==", "foo", "bar"])FILTER");
- const PropertyMap properties = { { "foo", std::string("bar") } };
+ const StubGeometryTileFeature feature = { {}, FeatureType::Unknown , {}, {{ "foo", std::string("bar") }} };
+ const style::expression::EvaluationContext context = { &feature };
while (state.KeepRunning()) {
- filter(FeatureType::Unknown, {}, [&] (const std::string& key) -> optional<Value> {
- auto it = properties.find(key);
- if (it == properties.end())
- return {};
- return it->second;
- });
+ filter(context);
}
}
diff --git a/benchmark/util/tilecover.benchmark.cpp b/benchmark/util/tilecover.benchmark.cpp
new file mode 100644
index 0000000000..186de6f216
--- /dev/null
+++ b/benchmark/util/tilecover.benchmark.cpp
@@ -0,0 +1,96 @@
+#include <benchmark/benchmark.h>
+#include <mbgl/util/geo.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/map/transform.hpp>
+
+using namespace mbgl;
+
+static const LatLngBounds sanFrancisco =
+ LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 });
+
+static void TileCountBounds(benchmark::State& state) {
+ std::size_t length = 0;
+ while (state.KeepRunning()) {
+ auto count = util::tileCount(sanFrancisco, 10);
+ length += count;
+ }
+}
+
+static void TileCoverPitchedViewport(benchmark::State& state) {
+ Transform transform;
+ transform.resize({ 512, 512 });
+ // slightly offset center so that tile order is better defined
+ transform.setLatLng({ 0.1, -0.1 });
+ transform.setZoom(8);
+ transform.setAngle(5.0);
+ transform.setPitch(40.0 * M_PI / 180.0);
+
+ std::size_t length = 0;
+ while (state.KeepRunning()) {
+ auto tiles = util::tileCover(transform.getState(), 8);
+ length += tiles.size();
+ }
+}
+
+static void TileCoverBounds(benchmark::State& state) {
+ std::size_t length = 0;
+ while (state.KeepRunning()) {
+ auto tiles = util::tileCover(sanFrancisco, 8);
+ length += tiles.size();
+ }
+}
+
+static const auto geomPolygon = Polygon<double>{
+ {
+ {-122.5143814086914,37.779127216982424},
+ {-122.50811576843262,37.72721239056709},
+ {-122.50313758850099,37.70820178063929},
+ {-122.3938751220703,37.707454835665274},
+ {-122.37567901611328,37.70663997801684},
+ {-122.36297607421874,37.71343018466285},
+ {-122.354736328125,37.727280276860036},
+ {-122.36469268798828,37.73868429065797},
+ {-122.38014221191408,37.75442980295571},
+ {-122.38391876220702,37.78753873820529},
+ {-122.35919952392578,37.8065289741725},
+ {-122.35679626464844,37.820632846207864},
+ {-122.3712158203125,37.835276322922695},
+ {-122.3818588256836,37.82958198283902},
+ {-122.37190246582031,37.80788523279169},
+ {-122.38735198974608,37.791337175930686},
+ {-122.40966796874999,37.812767557570204},
+ {-122.46425628662108,37.807071480609274},
+ {-122.46803283691405,37.810326435534755},
+ {-122.47901916503906,37.81168262440736},
+ {-122.48966217041016,37.78916666399649},
+ {-122.50579833984375,37.78781006166096},
+ {-122.5143814086914,37.779127216982424}
+ }
+};
+
+static void TileCoverPolygon(benchmark::State& state) {
+ std::size_t length = 0;
+
+ while (state.KeepRunning()) {
+ auto tiles = util::tileCover(geomPolygon, 8);
+ length += tiles.size();
+ }
+}
+
+static void TileCountPolygon(benchmark::State& state) {
+ std::size_t length = 0;
+
+ while (state.KeepRunning()) {
+ auto tiles = util::tileCount(geomPolygon, 16);
+ length += tiles;
+ }
+}
+
+BENCHMARK(TileCountBounds);
+BENCHMARK(TileCountPolygon);
+BENCHMARK(TileCoverPitchedViewport);
+BENCHMARK(TileCoverBounds);
+BENCHMARK(TileCoverPolygon);
+
diff --git a/bin/offline.cpp b/bin/offline.cpp
index ec2fb09096..65396a88fb 100644
--- a/bin/offline.cpp
+++ b/bin/offline.cpp
@@ -4,53 +4,66 @@
#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";
-
- 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::ArgumentParser argumentParser("Mapbox GL offline tool");
+ args::HelpFlag helpFlag(argumentParser, "help", "Display this help menu", {'h', "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 database file name", {'o', "output"});
+ args::ValueFlag<std::string> apiBaseValue(argumentParser, "URL", "API Base URL", {'a', "apiBaseURL"});
+
+ 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());
+
+ const std::string apiBaseURL = apiBaseValue ? args::get(apiBaseValue) : mbgl::util::API_BASE_URL;
+
using namespace mbgl;
util::RunLoop loop;
@@ -58,6 +71,7 @@ int main(int argc, char *argv[]) {
std::unique_ptr<OfflineRegion> region;
fileSource.setAccessToken(token);
+ fileSource.setAPIBaseURL(apiBaseURL);
LatLngBounds boundingBox = LatLngBounds::hull(LatLng(north, west), LatLng(south, east));
OfflineTilePyramidRegionDefinition definition(style, boundingBox, minZoom, maxZoom, pixelRatio);
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 f2e82d2a7b..d2232e9106 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
@@ -31,8 +32,9 @@ workflows:
- linux-gcc5-release-qt4
- linux-gcc5-release-qt5
- ios-debug
- #- ios-sanitize-address
- - ios-sanitize-thread
+ - ios-sanitize
+ - ios-sanitize-address
+ - ios-static-analyzer
- ios-release:
filters:
tags:
@@ -69,6 +71,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
@@ -120,6 +131,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
@@ -136,8 +151,7 @@ step-library:
run:
name: Install macOS dependencies
command: |
- brew install cmake
- brew install ccache
+ brew install cmake ccache
- &install-macos-node4-dependencies
run:
@@ -157,14 +171,12 @@ step-library:
run:
name: Install macOS Qt dependencies
command: |
- sudo chown -R $USER /usr/local
brew install qt
brew link qt --force
- brew linkapps qt
export HOMEBREW_QT5_CELLAR=$(brew --cellar qt)
export HOMEBREW_QT5_VERSION=$(brew list --versions qt | rev | cut -d' ' -f1 | rev)
- ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/mkspecs /usr/local/mkspecs
- ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/plugins /usr/local/plugins
+ sudo ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/mkspecs /usr/local/mkspecs
+ sudo ln -s $HOMEBREW_QT5_CELLAR/$HOMEBREW_QT5_VERSION/plugins /usr/local/plugins
- &run-node-macos-tests
run:
@@ -209,11 +221,64 @@ step-library:
path: mapbox-gl-js/test/integration/render-tests/index-recycle-map.html
destination: render-tests
+ - &collect-xcode-build-logs
+ run:
+ name: Collect Xcode build logs
+ when: always
+ command: |
+ export XCODE_LOG_DIR=build/logs
+ mkdir -p $XCODE_LOG_DIR
+ cp build/*.log $XCODE_LOG_DIR
+ - &upload-xcode-build-logs
+ store_artifacts:
+ path: build/logs
+
jobs:
+ nitpick:
+ docker:
+ - 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
@@ -240,7 +305,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:
@@ -252,6 +317,7 @@ jobs:
- checkout
- *generate-cache-key
- *restore-cache
+ - *restore-gradle-cache
- *reset-ccache-stats
- run:
name: Check code style
@@ -275,6 +341,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
@@ -288,23 +355,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
@@ -322,7 +378,7 @@ jobs:
# ------------------------------------------------------------------------------
android-release-all:
docker:
- - image: mbgl/ci@sha256:c34e221294d81da80918d3e9a9df5de795b067e88f86d7c9a5e262763332536e
+ - image: mbgl/7d2403f42e:android-ndk-r16b
resource_class: large
working_directory: /src
environment:
@@ -334,6 +390,7 @@ jobs:
- checkout
- *generate-cache-key
- *restore-cache
+ - *restore-gradle-cache
- *reset-ccache-stats
- run:
name: Generate Maven credentials
@@ -348,10 +405,14 @@ jobs:
- run:
name: Build package
command: make apackage
+ - run:
+ name: Build release Test App
+ command: make android
- *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
@@ -361,13 +422,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
@@ -390,7 +451,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
@@ -411,16 +472,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
@@ -434,31 +495,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
@@ -488,7 +547,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
@@ -518,7 +577,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
@@ -548,12 +607,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
@@ -573,12 +632,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
@@ -601,12 +660,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
@@ -629,12 +688,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:
@@ -655,12 +714,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.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -671,14 +730,44 @@ 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
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
+
+# ------------------------------------------------------------------------------
+ ios-sanitize:
+ macos:
+ xcode: "9.3.0"
+ environment:
+ BUILDTYPE: Debug
+ HOMEBREW_NO_AUTO_UPDATE: 1
+ steps:
+ - checkout
+ - *install-macos-dependencies
+ - *generate-cache-key
+ - *restore-cache
+ - *reset-ccache-stats
+ - run:
+ name: Build and run SDK unit tests with thread and undefined behavior sanitizers
+ command: make ios-sanitize
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
ios-sanitize-address:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -693,11 +782,13 @@ jobs:
command: make ios-sanitize-address
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
- ios-sanitize-thread:
+ ios-static-analyzer:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -708,15 +799,17 @@ jobs:
- *restore-cache
- *reset-ccache-stats
- run:
- name: Build and run SDK unit tests with thread sanitizer
- command: make ios-sanitize-thread
+ name: Build and run SDK unit tests with the static analyzer
+ command: make ios-static-analyzer
- *show-ccache-stats
- *save-cache
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
ios-release:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
HOMEBREW_NO_AUTO_UPDATE: 1
shell: /bin/bash --login -eo pipefail
@@ -744,7 +837,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-debug:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -756,16 +849,24 @@ 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:
path: test/fixtures
destination: test/fixtures
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
macos-debug-qt5:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: Debug
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -790,7 +891,7 @@ jobs:
# ------------------------------------------------------------------------------
macos-release-node4:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: RelWithDebInfo
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -807,11 +908,13 @@ jobs:
- *run-node-macos-tests
- *publish-node-package
- *upload-render-tests
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
# ------------------------------------------------------------------------------
macos-release-node6:
macos:
- xcode: "9.2.0"
+ xcode: "9.3.0"
environment:
BUILDTYPE: RelWithDebInfo
HOMEBREW_NO_AUTO_UPDATE: 1
@@ -828,3 +931,5 @@ jobs:
- *run-node-macos-tests
- *publish-node-package
- *upload-render-tests
+ - *collect-xcode-build-logs
+ - *upload-xcode-build-logs
diff --git a/cmake/benchmark-files.cmake b/cmake/benchmark-files.cmake
index 9161209128..21547eb9c4 100644
--- a/cmake/benchmark-files.cmake
+++ b/cmake/benchmark-files.cmake
@@ -1,30 +1,28 @@
-# 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
+ benchmark/util/tilecover.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..0474de4680 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,10 @@ 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/filter.cpp
+ src/mbgl/style/filter_evaluator.cpp
src/mbgl/style/image.cpp
src/mbgl/style/image_impl.cpp
src/mbgl/style/image_impl.hpp
@@ -363,23 +408,87 @@ 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/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/length.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/length.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 +497,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 +512,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 +540,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 +567,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 +596,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 +613,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 +623,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 +634,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 +657,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 +692,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
@@ -609,11 +748,11 @@ set(MBGL_CORE_FILES
src/mbgl/util/stopwatch.hpp
src/mbgl/util/string.cpp
src/mbgl/util/thread_local.hpp
- src/mbgl/util/throttler.cpp
- src/mbgl/util/throttler.hpp
src/mbgl/util/tile_coordinate.hpp
src/mbgl/util/tile_cover.cpp
src/mbgl/util/tile_cover.hpp
+ src/mbgl/util/tile_cover_impl.cpp
+ src/mbgl/util/tile_cover_impl.hpp
src/mbgl/util/tile_range.hpp
src/mbgl/util/tiny_sdf.cpp
src/mbgl/util/tiny_sdf.hpp
@@ -624,4 +763,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
deleted file mode 100644
index 8840040963..0000000000
--- a/cmake/loop-uv.cmake
+++ /dev/null
@@ -1,23 +0,0 @@
-add_library(mbgl-loop-uv STATIC
- platform/default/async_task.cpp
- platform/default/run_loop.cpp
- 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
-)
-
-target_link_libraries(mbgl-loop-uv
- PRIVATE mbgl-core
-)
-
-create_source_groups(mbgl-loop-uv)
-
-xcode_create_scheme(TARGET mbgl-loop-uv) \ No newline at end of file
diff --git a/cmake/mbgl.cmake b/cmake/mbgl.cmake
index d853950c0d..f087c32511 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/mapbox-events-ios 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..90bec6575c 100644
--- a/cmake/node.cmake
+++ b/cmake/node.cmake
@@ -2,6 +2,22 @@
include(cmake/NodeJS.cmake)
nodejs_init()
+add_library(mbgl-loop-node STATIC
+ platform/default/async_task.cpp
+ platform/default/run_loop.cpp
+ platform/default/timer.cpp
+)
+
+target_include_directories(mbgl-loop-node
+ PRIVATE include
+ PRIVATE src
+)
+
+target_include_directories(mbgl-loop-node PUBLIC ${NODEJS_INCLUDE_DIRS})
+
+create_source_groups(mbgl-loop-node)
+xcode_create_scheme(TARGET mbgl-loop-node)
+
add_nodejs_module(mbgl-node
platform/node/src/node_mapbox_gl_native.cpp
)
@@ -13,6 +29,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,25 +38,18 @@ 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
)
-# Use node-provided uv.h. This is not part of loop-uv.cmake because loop-uv.cmake is also
-# used by linux/config.cmake, where we need to use headers provided by mason's libuv.
-target_include_directories(mbgl-loop-uv PUBLIC ${NODEJS_INCLUDE_DIRS})
-
target_link_libraries(mbgl-node
PRIVATE mbgl-core
- PRIVATE mbgl-loop-uv
+ PRIVATE mbgl-loop-node
)
target_add_mason_package(mbgl-node PRIVATE geojson)
@@ -93,6 +103,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..2aadfa8a7f 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,37 @@ 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_map_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 +133,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 +151,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..1d5f4e8f4e 100644
--- a/include/mbgl/renderer/renderer_backend.hpp
+++ b/include/mbgl/renderer/renderer_backend.hpp
@@ -41,16 +41,16 @@ 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:
//
// 1. When releasing GL resources during Renderer destruction
- // (Including calling CustomLayerDeinitializeFunction during RenderCustomLayer destruction)
+ // (Including calling CustomLayerHost::deinitialize during RenderCustomLayer destruction)
// 2. When renderering through Renderer::render()
- // (Including calling CustomLayerDeinitializeFunction for newly added custom layers and
- // CustomLayerDeinitializeFunction on layer removal)
+ // (Including calling CustomLayerHost::initialize for newly added custom layers and
+ // CustomLayerHost::deinitialize on layer removal)
virtual void activate() = 0;
virtual void deactivate() = 0;
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..07ed201c99 100644
--- a/include/mbgl/style/conversion/data_driven_property_value.hpp
+++ b/include/mbgl/style/conversion/data_driven_property_value.hpp
@@ -4,6 +4,15 @@
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/conversion/constant.hpp>
#include <mbgl/style/conversion/function.hpp>
+#include <mbgl/style/expression/is_expression.hpp>
+#include <mbgl/style/expression/is_constant.hpp>
+#include <mbgl/style/expression/find_zoom_curve.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+#include <unordered_set>
+
namespace mbgl {
namespace style {
@@ -11,10 +20,38 @@ 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 {
+ using namespace mbgl::style::expression;
+
if (isUndefined(value)) {
return DataDrivenPropertyValue<T>();
+ } else if (isExpression(value)) {
+ ParsingContext ctx(valueTypeToExpressionType<T>());
+ ParseResult expression = ctx.parseLayerPropertyExpression(value);
+ if (!expression) {
+ error = { ctx.getCombinedErrors() };
+ return {};
+ }
+
+ bool featureConstant = isFeatureConstant(**expression);
+ bool zoomConstant = isZoomConstant(**expression);
+
+ if (featureConstant && !zoomConstant) {
+ return DataDrivenPropertyValue<T>(CameraFunction<T>(std::move(*expression)));
+ } else if (!featureConstant && zoomConstant) {
+ return DataDrivenPropertyValue<T>(SourceFunction<T>(std::move(*expression)));
+ } else if (!featureConstant && !zoomConstant) {
+ return DataDrivenPropertyValue<T>(CompositeFunction<T>(std::move(*expression)));
+ } else {
+ auto literal = dynamic_cast<Literal*>(expression->get());
+ assert(literal);
+ optional<T> constant = fromExpressionValue<T>(literal->getValue());
+ if (!constant) {
+ return {};
+ }
+ return DataDrivenPropertyValue<T>(*constant);
+ }
} else if (!isObject(value)) {
optional<T> constant = convert<T>(value, error);
if (!constant) {
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..a4710792d2
--- /dev/null
+++ b/include/mbgl/style/conversion/heatmap_color_property_value.hpp
@@ -0,0 +1,49 @@
+#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/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>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+struct Converter<HeatmapColorPropertyValue> {
+ optional<HeatmapColorPropertyValue> operator()(const Convertible& value, Error& error) const {
+ using namespace mbgl::style::expression;
+ if (isUndefined(value)) {
+ return HeatmapColorPropertyValue();
+ } else if (isExpression(value)) {
+ ParsingContext ctx(type::Color);
+ ParseResult expression = ctx.parseLayerPropertyExpression(value);
+ if (!expression) {
+ error = { ctx.getCombinedErrors() };
+ 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..3130661f61 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/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>
+#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
namespace style {
@@ -11,10 +16,25 @@ 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 {
+ using namespace mbgl::style::expression;
+
if (isUndefined(value)) {
return PropertyValue<T>();
+ } else if (isExpression(value)) {
+ ParsingContext ctx(valueTypeToExpressionType<T>());
+ ParseResult expression = ctx.parseLayerPropertyExpression(value);
+ if (!expression) {
+ error = { ctx.getCombinedErrors() };
+ 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 16322b58ee..1fb4acf70d 100644
--- a/include/mbgl/style/conversion/tileset.hpp
+++ b/include/mbgl/style/conversion/tileset.hpp
@@ -10,103 +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 {};
- }
- *left = util::max(-180.0, *left);
- *right = util::min(180.0, *right);
- 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/data_driven_property_value.hpp b/include/mbgl/style/data_driven_property_value.hpp
index 5d7c596363..0a1fce29c7 100644
--- a/include/mbgl/style/data_driven_property_value.hpp
+++ b/include/mbgl/style/data_driven_property_value.hpp
@@ -50,6 +50,15 @@ public:
return !value.template is<CameraFunction<T>>() && !value.template is<CompositeFunction<T>>();
}
+ bool isExpression() const {
+ return value.match(
+ [] (const Undefined&) { return false; },
+ [] (const T&) { return false; },
+ [] (const CameraFunction<T>& fn) { return fn.isExpression; },
+ [] (const SourceFunction<T>& fn) { return fn.isExpression; },
+ [] (const CompositeFunction<T>& fn) { return fn.isExpression; });
+ }
+
template <class... Ts>
auto match(Ts&&... ts) const {
return value.match(std::forward<Ts>(ts)...);
diff --git a/include/mbgl/style/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/length.hpp b/include/mbgl/style/expression/length.hpp
new file mode 100644
index 0000000000..1d754f1932
--- /dev/null
+++ b/include/mbgl/style/expression/length.hpp
@@ -0,0 +1,32 @@
+#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 Length : public Expression {
+public:
+ Length(std::unique_ptr<Expression> 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;
+ std::vector<optional<Value>> possibleOutputs() const override;
+ std::string getOperator() const override { return "length"; }
+
+private:
+ std::unique_ptr<Expression> input;
+};
+
+} // 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..d0210d8bba
--- /dev/null
+++ b/include/mbgl/style/expression/let.hpp
@@ -0,0 +1,83 @@
+#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"; }
+
+ const std::shared_ptr<Expression>& getBoundExpression() const { return value; }
+
+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..a00c468efc
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,56 @@
+#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 }};
+ }
+
+ Value getValue() const {
+ 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..c19974a4f7
--- /dev/null
+++ b/include/mbgl/style/expression/parsing_context.hpp
@@ -0,0 +1,172 @@
+#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(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; }
+ const std::string getCombinedErrors() const;
+
+ enum TypeAnnotationOption {
+ includeTypeAnnotations,
+ omitTypeAnnotations
+ };
+
+ /*
+ Parse the given style-spec JSON value as an expression.
+ */
+ ParseResult parseExpression(const mbgl::style::conversion::Convertible& value,
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse the given style-spec JSON value as an expression intended to be used
+ in a layout or paint property. This entails checking additional constraints
+ that exist in that context but not, e.g., for filters.
+ */
+ ParseResult parseLayerPropertyExpression(const mbgl::style::conversion::Convertible& value,
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression. For use by individual Expression::parse() methods.
+ */
+ ParseResult parse(const mbgl::style::conversion::Convertible&,
+ std::size_t,
+ optional<type::Type> = {},
+ TypeAnnotationOption typeAnnotationOption = includeTypeAnnotations);
+
+ /*
+ Parse a child expression. For use by individual Expression::parse() methods.
+ */
+ 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_))
+ {}
+
+
+ /*
+ 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);
+
+ 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/filter.hpp b/include/mbgl/style/filter.hpp
index a204a2b17a..ccf8dce188 100644
--- a/include/mbgl/style/filter.hpp
+++ b/include/mbgl/style/filter.hpp
@@ -3,6 +3,7 @@
#include <mbgl/util/variant.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/expression/expression.hpp>
#include <string>
#include <vector>
@@ -232,6 +233,15 @@ public:
return true;
}
};
+
+class ExpressionFilter {
+public:
+ std::shared_ptr<const expression::Expression> expression;
+
+ friend bool operator==(const ExpressionFilter& lhs, const ExpressionFilter& rhs) {
+ return *(lhs.expression) == *(rhs.expression);
+ }
+};
using FilterBase = variant<
@@ -258,19 +268,13 @@ using FilterBase = variant<
class IdentifierInFilter,
class IdentifierNotInFilter,
class HasIdentifierFilter,
- class NotHasIdentifierFilter>;
+ class NotHasIdentifierFilter,
+ class ExpressionFilter>;
class Filter : public FilterBase {
public:
using FilterBase::FilterBase;
-
- bool operator()(const Feature&) const;
-
- template <class GeometryTileFeature>
- bool operator()(const GeometryTileFeature&) const;
-
- template <class PropertyAccessor>
- bool operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const;
+ bool operator()(const expression::EvaluationContext& context) const;
};
} // namespace style
diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp
index 66223d7282..a4a4098b3f 100644
--- a/include/mbgl/style/filter_evaluator.hpp
+++ b/include/mbgl/style/filter_evaluator.hpp
@@ -19,242 +19,37 @@ namespace style {
// does not match
}
*/
-template <class PropertyAccessor>
class FilterEvaluator {
public:
- const FeatureType featureType;
- const optional<FeatureIdentifier> featureIdentifier;
- const PropertyAccessor propertyAccessor;
+ const expression::EvaluationContext context;
+
+ bool operator()(const NullFilter&) const;
+ bool operator()(const EqualsFilter& filter) const;
+ bool operator()(const NotEqualsFilter& filter) const;
+ bool operator()(const LessThanFilter& filter) const;
+ bool operator()(const LessThanEqualsFilter& filter) const;
+ bool operator()(const GreaterThanFilter& filter) const;
+ bool operator()(const GreaterThanEqualsFilter& filter) const;
+ bool operator()(const InFilter& filter) const;
+ bool operator()(const NotInFilter& filter) const;
+ bool operator()(const AnyFilter& filter) const;
+ bool operator()(const AllFilter& filter) const;
+ bool operator()(const NoneFilter& filter) const;
+ bool operator()(const HasFilter& filter) const;
+ bool operator()(const NotHasFilter& filter) const;
+ bool operator()(const TypeEqualsFilter& filter) const;
+ bool operator()(const TypeNotEqualsFilter& filter) const;
+ bool operator()(const TypeInFilter& filter) const;
+ bool operator()(const TypeNotInFilter& filter) const;
+ bool operator()(const IdentifierEqualsFilter& filter) const;
+ bool operator()(const IdentifierNotEqualsFilter& filter) const;
+ bool operator()(const IdentifierInFilter& filter) const;
+ bool operator()(const IdentifierNotInFilter& filter) const;
+ bool operator()(const HasIdentifierFilter&) const;
+ bool operator()(const NotHasIdentifierFilter&) const;
+ bool operator()(const ExpressionFilter&) const;
- bool operator()(const NullFilter&) const {
- return true;
- }
-
- bool operator()(const EqualsFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return actual && equal(*actual, filter.value);
- }
-
- bool operator()(const NotEqualsFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return !actual || !equal(*actual, filter.value);
- }
-
- bool operator()(const LessThanFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; });
- }
-
- bool operator()(const LessThanEqualsFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; });
- }
-
- bool operator()(const GreaterThanFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; });
- }
-
- bool operator()(const GreaterThanEqualsFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; });
- }
-
- bool operator()(const InFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- if (!actual)
- return false;
- for (const auto& v: filter.values) {
- if (equal(*actual, v)) {
- return true;
- }
- }
- return false;
- }
-
- bool operator()(const NotInFilter& filter) const {
- optional<Value> actual = propertyAccessor(filter.key);
- if (!actual)
- return true;
- for (const auto& v: filter.values) {
- if (equal(*actual, v)) {
- return false;
- }
- }
- return true;
- }
-
- bool operator()(const AnyFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (Filter::visit(f, *this)) {
- return true;
- }
- }
- return false;
- }
-
- bool operator()(const AllFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (!Filter::visit(f, *this)) {
- return false;
- }
- }
- return true;
- }
-
- bool operator()(const NoneFilter& filter) const {
- for (const auto& f: filter.filters) {
- if (Filter::visit(f, *this)) {
- return false;
- }
- }
- return true;
- }
-
- bool operator()(const HasFilter& filter) const {
- return bool(propertyAccessor(filter.key));
- }
-
- bool operator()(const NotHasFilter& filter) const {
- return !propertyAccessor(filter.key);
- }
-
-
- bool operator()(const TypeEqualsFilter& filter) const {
- return featureType == filter.value;
- }
-
- bool operator()(const TypeNotEqualsFilter& filter) const {
- return featureType != filter.value;
- }
-
- bool operator()(const TypeInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (featureType == v) {
- return true;
- }
- }
- return false;
- }
-
- bool operator()(const TypeNotInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (featureType == v) {
- return false;
- }
- }
- return true;
- }
-
-
- bool operator()(const IdentifierEqualsFilter& filter) const {
- return featureIdentifier == filter.value;
- }
-
- bool operator()(const IdentifierNotEqualsFilter& filter) const {
- return featureIdentifier != filter.value;
- }
-
- bool operator()(const IdentifierInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (featureIdentifier == v) {
- return true;
- }
- }
- return false;
- }
-
- bool operator()(const IdentifierNotInFilter& filter) const {
- for (const auto& v: filter.values) {
- if (featureIdentifier == v) {
- return false;
- }
- }
- return true;
- }
-
- bool operator()(const HasIdentifierFilter&) const {
- return bool(featureIdentifier);
- }
-
- bool operator()(const NotHasIdentifierFilter&) const {
- return !featureIdentifier;
- }
-
-private:
- template <class Op>
- struct Comparator {
- const Op& op;
-
- template <class T>
- bool operator()(const T& lhs, const T& rhs) const {
- return op(lhs, rhs);
- }
-
- template <class T0, class T1>
- auto operator()(const T0& lhs, const T1& rhs) const
- -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
- std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
- return op(double(lhs), double(rhs));
- }
-
- template <class T0, class T1>
- auto operator()(const T0&, const T1&) const
- -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
- !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::value, bool> {
- return false;
- }
-
- bool operator()(const NullValue&,
- const NullValue&) const {
- // Should be unreachable; null is not currently allowed by the style specification.
- assert(false);
- return false;
- }
-
- bool operator()(const std::vector<Value>&,
- const std::vector<Value>&) const {
- // Should be unreachable; nested values are not currently allowed by the style specification.
- assert(false);
- return false;
- }
-
- bool operator()(const PropertyMap&,
- const PropertyMap&) const {
- // Should be unreachable; nested values are not currently allowed by the style specification.
- assert(false);
- return false;
- }
- };
-
- template <class Op>
- bool compare(const Value& lhs, const Value& rhs, const Op& op) const {
- return Value::binary_visit(lhs, rhs, Comparator<Op> { op });
- }
-
- bool equal(const Value& lhs, const Value& rhs) const {
- return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; });
- }
};
-inline bool Filter::operator()(const Feature& feature) const {
- return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional<Value> {
- auto it = feature.properties.find(key);
- if (it == feature.properties.end())
- return {};
- return it->second;
- });
-}
-
-template <class GeometryTileFeature>
-bool Filter::operator()(const GeometryTileFeature& feature) const {
- return operator()(feature.getType(), feature.getID(), [&] (const auto& key) { return feature.getValue(key); });
-}
-
-template <class PropertyAccessor>
-bool Filter::operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const {
- return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { type, id, accessor });
-}
-
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 7fde365b3d..97ba633e44 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,65 @@ public:
IntervalStops<T>>,
variant<
IntervalStops<T>>>;
-
- CameraFunction(Stops stops_)
- : stops(std::move(stops_)) {
+
+ CameraFunction(std::unique_ptr<expression::Expression> expression_)
+ : isExpression(true),
+ expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(expression::isFeatureConstant(*expression));
}
+ CameraFunction(const Stops& stops)
+ : isExpression(false),
+ 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;
+ bool isExpression;
+
+ const expression::Expression& getExpression() const { return *expression; }
+
+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..614c345c25 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,76 @@ 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_)) {
+ CompositeFunction(std::unique_ptr<expression::Expression> expression_)
+ : isExpression(true),
+ expression(std::move(expression_)),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {
+ assert(!expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
}
- 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(const std::string& property, const Stops& stops, optional<T> defaultValue_ = {})
+ : isExpression(false),
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(property, s);
+ })),
+ zoomCurve(expression::findZoomCurveChecked(expression.get()))
+ {}
- // 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.
+ // 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;
}
- std::string property;
- Stops stops;
- optional<T> defaultValue;
- bool useIntegerZoom = false;
+ const expression::Expression& getExpression() const { return *expression; }
+ bool useIntegerZoom = false;
+ bool isExpression;
+
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));
- }
+ optional<T> defaultValue;
+ 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..5b51d0bf81 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,51 @@ public:
CategoricalStops<T>,
IdentityStops<T>>>;
- SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
- : property(std::move(property_)),
- stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
+ SourceFunction(std::unique_ptr<expression::Expression> expression_)
+ : isExpression(true),
+ expression(std::move(expression_))
+ {
+ assert(expression::isZoomConstant(*expression));
+ assert(!expression::isFeatureConstant(*expression));
}
+
+ SourceFunction(const std::string& property, const Stops& stops, optional<T> defaultValue_ = {})
+ : isExpression(false),
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const IdentityStops<T>&) {
+ return expression::Convert::fromIdentityFunction(expression::valueTypeToExpressionType<T>(), property);
+ }, [&] (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;
}
- std::string property;
- Stops stops;
- optional<T> defaultValue;
bool useIntegerZoom = false;
+ bool isExpression;
+
+ const expression::Expression& getExpression() const { return *expression; }
+
+private:
+ optional<T> defaultValue;
+ 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/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp
index bf3387f95b..fbe3a4a6c2 100644
--- a/include/mbgl/style/layers/custom_layer.hpp
+++ b/include/mbgl/style/layers/custom_layer.hpp
@@ -2,20 +2,13 @@
#include <mbgl/style/layer.hpp>
+#include <array>
+
namespace mbgl {
namespace style {
/**
- * Initialize any GL state needed by the custom layer. This method is called once, from the
- * main thread, at a point when the GL context is active but before rendering for the first
- * time.
- *
- * Resources that are acquired in this method must be released in the UninitializeFunction.
- */
-using CustomLayerInitializeFunction = void (*)(void* context);
-
-/**
- * Parameters that define the current camera position for a CustomLayerRenderFunction.
+ * Parameters that define the current camera position for a `CustomLayerHost::render()` function.
*/
struct CustomLayerRenderParameters {
double width;
@@ -26,48 +19,52 @@ struct CustomLayerRenderParameters {
double bearing;
double pitch;
double fieldOfView;
+ std::array<double, 16> projectionMatrix;
};
-/**
- * Render the layer. This method is called once per frame. The implementation should not make
- * any assumptions about the GL state (other than that the correct context is active). It may
- * make changes to the state, and is not required to reset values such as the depth mask, stencil
- * mask, and corresponding test flags to their original values.
- * Make sure that you are drawing your fragments with a z value of 1 to take advantage of the
- * opaque fragment culling in case there are opaque layers above your custom layer.
- */
-using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&);
-
-/**
- * Called when the system has destroyed the underlying GL context. The
- * `CustomLayerDeinitializeFunction` will not be called in this case, however
- * `CustomLayerInitializeFunction` will be called instead to prepare for a new render.
- *
- */
-using CustomLayerContextLostFunction = void (*)(void* context);
-
-/**
- * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
- * method is called once, from the main thread, at a point when the GL context is active.
- *
- * Note that it may be called even when the InitializeFunction has not been called.
- */
-using CustomLayerDeinitializeFunction = void (*)(void* context);
+class CustomLayerHost {
+public:
+ virtual ~CustomLayerHost() = default;
+ /**
+ * Initialize any GL state needed by the custom layer. This method is called once, from the
+ * main thread, at a point when the GL context is active but before rendering for the first
+ * time.
+ *
+ * Resources that are acquired in this method must be released in the `deinitialize` function.
+ */
+ virtual void initialize() = 0;
+
+ /**
+ * Render the layer. This method is called once per frame. The implementation should not make
+ * any assumptions about the GL state (other than that the correct context is active). It may
+ * make changes to the state, and is not required to reset values such as the depth mask, stencil
+ * mask, and corresponding test flags to their original values.
+ * Make sure that you are drawing your fragments with a z value of 1 to take advantage of the
+ * opaque fragment culling in case there are opaque layers above your custom layer.
+ */
+ virtual void render(const CustomLayerRenderParameters&) = 0;
+
+ /**
+ * Called when the system has destroyed the underlying GL context. The
+ * `deinitialize` function will not be called in this case, however
+ * `initialize` will be called instead to prepare for a new render.
+ *
+ */
+ virtual void contextLost() = 0;
+
+ /**
+ * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
+ * method is called once, from the main thread, at a point when the GL context is active.
+ *
+ * Note that it may be called even when the `initialize` function has not been called.
+ */
+ virtual void deinitialize() = 0;
+};
class CustomLayer : public Layer {
public:
CustomLayer(const std::string& id,
- CustomLayerInitializeFunction,
- CustomLayerRenderFunction,
- CustomLayerContextLostFunction,
- CustomLayerDeinitializeFunction,
- void* context);
-
- CustomLayer(const std::string& id,
- CustomLayerInitializeFunction,
- CustomLayerRenderFunction,
- CustomLayerDeinitializeFunction,
- void* context);
+ std::unique_ptr<CustomLayerHost> host);
~CustomLayer() final;
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/color.hpp b/include/mbgl/util/color.hpp
index 300d7fae82..01a4c8f292 100644
--- a/include/mbgl/util/color.hpp
+++ b/include/mbgl/util/color.hpp
@@ -4,6 +4,7 @@
#include <cassert>
#include <string>
+#include <array>
namespace mbgl {
@@ -37,6 +38,7 @@ public:
static optional<Color> parse(const std::string&);
std::string stringify() const;
+ std::array<double, 4> toArray() const;
};
inline bool operator==(const Color& colorA, const Color& colorB) {
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..65a84d8dc2 100644
--- a/include/mbgl/util/projection.hpp
+++ b/include/mbgl/util/projection.hpp
@@ -78,8 +78,9 @@ public:
return project_(latLng, worldSize(scale));
}
- static Point<double> project(const LatLng& latLng, uint8_t zoom) {
- return project_(latLng, std::pow(2.0, zoom));
+ //Returns point on tile
+ static Point<double> project(const LatLng& latLng, int32_t zoom) {
+ return project_(latLng, 1 << zoom);
}
static LatLng unproject(const Point<double>& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) {
@@ -90,16 +91,7 @@ public:
wrapMode
};
}
-
- // Project lat, lon to point in a zoom-dependent world size
- static Point<double> project(const LatLng& point, uint8_t zoom, uint16_t tileSize) {
- 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));
- return { x, y };
- }
+
private:
static Point<double> project_(const LatLng& latLng, double worldSize) {
return Point<double> {
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 5f3bdb9b00915d702ae9b967c5ebc15ab33ba65
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..2ec7884afd 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: Mon Apr 16 12:40:48 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: 704f02707ec6b4c4a7597a8c6039b020def11e64f3ef0605a9c3543d48038a57
##
@@ -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,86 +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-----
-MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
-QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
-ZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAx
-NjEzNDNaFw0zNzA5MzAxNjEzNDRaMH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZp
-cm1hIFNBIENJRiBBODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3Jn
-MSIwIAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0BAQEFAAOC
-AQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtbunXF/KGIJPov7coISjlU
-xFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0dBmpAPrMMhe5cG3nCYsS4No41XQEMIwRH
-NaqbYE6gZj3LJgqcQKH0XZi/caulAGgq7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jW
-DA+wWFjbw2Y3npuRVDM30pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFV
-d9oKDMyXroDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIGA1Ud
-EwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5jaGFtYmVyc2lnbi5v
-cmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p26EpW1eLTXYGduHRooowDgYDVR0P
-AQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hh
-bWJlcnNpZ24ub3JnMCcGA1UdEgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYD
-VR0gBFEwTzBNBgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz
-aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEBAAxBl8IahsAi
-fJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZdp0AJPaxJRUXcLo0waLIJuvvD
-L8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wN
-UPf6s+xCX6ndbcj0dc97wXImsQEcXCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/n
-ADydb47kMgkdTXg0eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1
-erfutGWaIZDgqtCYvDi1czyL+Nw=
------END CERTIFICATE-----
-
-Camerfirma Global Chambersign Root
-==================================
------BEGIN CERTIFICATE-----
-MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMe
-QUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1i
-ZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYx
-NDE4WhcNMzcwOTMwMTYxNDE4WjB9MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJt
-YSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEg
-MB4GA1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUAA4IBDQAw
-ggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0Mi+ITaFgCPS3CU6gSS9J
-1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/sQJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8O
-by4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpVeAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl
-6DJWk0aJqCWKZQbua795B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c
-8lCrEqWhz0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0TAQH/
-BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1iZXJzaWduLm9yZy9j
-aGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4wTcbOX60Qq+UDpfqpFDAOBgNVHQ8B
-Af8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAHMCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBj
-aGFtYmVyc2lnbi5vcmcwKgYDVR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9y
-ZzBbBgNVHSAEVDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh
-bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0BAQUFAAOCAQEA
-PDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUMbKGKfKX0j//U2K0X1S0E0T9Y
-gOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXiryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJ
-PJ7oKXqJ1/6v/2j1pReQvayZzKWGVwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4
-IBHNfTIzSJRUTN3cecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREes
-t2d/AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A==
------END CERTIFICATE-----
-
XRamp Global CA Root
====================
-----BEGIN CERTIFICATE-----
@@ -760,47 +518,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 +548,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-----
@@ -971,30 +656,6 @@ RLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubS
fZGL+T0yjWW06XyxV3bqxbYoOb8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ
-----END CERTIFICATE-----
-DST ACES CA X6
-==============
------BEGIN CERTIFICATE-----
-MIIECTCCAvGgAwIBAgIQDV6ZCtadt3js2AdWO4YV2TANBgkqhkiG9w0BAQUFADBbMQswCQYDVQQG
-EwJVUzEgMB4GA1UEChMXRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QxETAPBgNVBAsTCERTVCBBQ0VT
-MRcwFQYDVQQDEw5EU1QgQUNFUyBDQSBYNjAeFw0wMzExMjAyMTE5NThaFw0xNzExMjAyMTE5NTha
-MFsxCzAJBgNVBAYTAlVTMSAwHgYDVQQKExdEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdDERMA8GA1UE
-CxMIRFNUIEFDRVMxFzAVBgNVBAMTDkRTVCBBQ0VTIENBIFg2MIIBIjANBgkqhkiG9w0BAQEFAAOC
-AQ8AMIIBCgKCAQEAuT31LMmU3HWKlV1j6IR3dma5WZFcRt2SPp/5DgO0PWGSvSMmtWPuktKe1jzI
-DZBfZIGxqAgNTNj50wUoUrQBJcWVHAx+PhCEdc/BGZFjz+iokYi5Q1K7gLFViYsx+tC3dr5BPTCa
-pCIlF3PoHuLTrCq9Wzgh1SpL11V94zpVvddtawJXa+ZHfAjIgrrep4c9oW24MFbCswKBXy314pow
-GCi4ZtPLAZZv6opFVdbgnf9nKxcCpk4aahELfrd755jWjHZvwTvbUJN+5dCOHze4vbrGn2zpfDPy
-MjwmR/onJALJfh1biEITajV8fTXpLmaRcpPVMibEdPVTo7NdmvYJywIDAQABo4HIMIHFMA8GA1Ud
-EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgHGMB8GA1UdEQQYMBaBFHBraS1vcHNAdHJ1c3Rkc3Qu
-Y29tMGIGA1UdIARbMFkwVwYKYIZIAWUDAgEBATBJMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnRy
-dXN0ZHN0LmNvbS9jZXJ0aWZpY2F0ZXMvcG9saWN5L0FDRVMtaW5kZXguaHRtbDAdBgNVHQ4EFgQU
-CXIGThhDD+XWzMNqizF7eI+og7gwDQYJKoZIhvcNAQEFBQADggEBAKPYjtay284F5zLNAdMEA+V2
-5FYrnJmQ6AgwbN99Pe7lv7UkQIRJ4dEorsTCOlMwiPH1d25Ryvr/ma8kXxug/fKshMrfqfBfBC6t
-Fr8hlxCBPeP/h40y3JTlR4peahPJlJU90u7INJXQgNStMgiAVDzgvVJT11J8smk/f3rPanTK+gQq
-nExaBqXpIK1FZg9p8d2/6eMyi/rgwYZNcjwu2JN4Cir42NInPRmJX1p7ijvMDNpRrscL9yuwNwXs
-vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3
-oKfN5XozNmr6mis=
------END CERTIFICATE-----
-
SwissSign Gold CA - G2
======================
-----BEGIN CERTIFICATE-----
@@ -1220,33 +881,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-----
@@ -1264,27 +898,6 @@ FAkK+qDmfQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdvGDeA
U/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY=
-----END CERTIFICATE-----
-Security Communication EV RootCA1
-=================================
------BEGIN CERTIFICATE-----
-MIIDfTCCAmWgAwIBAgIBADANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJKUDElMCMGA1UEChMc
-U0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEqMCgGA1UECxMhU2VjdXJpdHkgQ29tbXVuaWNh
-dGlvbiBFViBSb290Q0ExMB4XDTA3MDYwNjAyMTIzMloXDTM3MDYwNjAyMTIzMlowYDELMAkGA1UE
-BhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKjAoBgNVBAsTIVNl
-Y3VyaXR5IENvbW11bmljYXRpb24gRVYgUm9vdENBMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
-AQoCggEBALx/7FebJOD+nLpCeamIivqA4PUHKUPqjgo0No0c+qe1OXj/l3X3L+SqawSERMqm4miO
-/VVQYg+kcQ7OBzgtQoVQrTyWb4vVog7P3kmJPdZkLjjlHmy1V4qe70gOzXppFodEtZDkBp2uoQSX
-WHnvIEqCa4wiv+wfD+mEce3xDuS4GBPMVjZd0ZoeUWs5bmB2iDQL87PRsJ3KYeJkHcFGB7hj3R4z
-ZbOOCVVSPbW9/wfrrWFVGCypaZhKqkDFMxRldAD5kd6vA0jFQFTcD4SQaCDFkpbcLuUCRarAX1T4
-bepJz11sS6/vmsJWXMY1VkJqMF/Cq/biPT+zyRGPMUzXn0kCAwEAAaNCMEAwHQYDVR0OBBYEFDVK
-9U2vP9eCOKyrcWUXdYydVZPmMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
-SIb3DQEBBQUAA4IBAQCoh+ns+EBnXcPBZsdAS5f8hxOQWsTvoMpfi7ent/HWtWS3irO4G8za+6xm
-iEHO6Pzk2x6Ipu0nUBsCMCRGef4Eh3CXQHPRwMFXGZpppSeZq51ihPZRwSzJIxXYKLerJRO1RuGG
-Av8mjMSIkh1W/hln8lXkgKNrnKt34VFxDSDbEJrbvXZ5B3eZKK2aXtqxT0QsNY6llsf9g/BYxnnW
-mHyojf6GPgcWkuF75x3sM3Z+Qi5KhfmRiWiEA4Glm5q+4zfFVKtWOxgtQaQM+ELbmaDgcm+7XeEW
-T1MKZPlO9L9OVL14bIjqv5wTJMJwaaJ/D8g8rQjJsJhAoyrniIPtd490
------END CERTIFICATE-----
-
OISTE WISeKey Global Root GA CA
===============================
-----BEGIN CERTIFICATE-----
@@ -1308,46 +921,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 +1018,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 +1038,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 +1265,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 +1625,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 +1773,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 +1879,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,171 +1928,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-----
-MIIFaTCCA1GgAwIBAgIJAMMDmu5QkG4oMA0GCSqGSIb3DQEBBQUAMFIxCzAJBgNVBAYTAlNLMRMw
-EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMuMRkwFwYDVQQDExBDQSBEaXNp
-ZyBSb290IFIxMB4XDTEyMDcxOTA5MDY1NloXDTQyMDcxOTA5MDY1NlowUjELMAkGA1UEBhMCU0sx
-EzARBgNVBAcTCkJyYXRpc2xhdmExEzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERp
-c2lnIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCqw3j33Jijp1pedxiy
-3QRkD2P9m5YJgNXoqqXinCaUOuiZc4yd39ffg/N4T0Dhf9Kn0uXKE5Pn7cZ3Xza1lK/oOI7bm+V8
-u8yN63Vz4STN5qctGS7Y1oprFOsIYgrY3LMATcMjfF9DCCMyEtztDK3AfQ+lekLZWnDZv6fXARz2
-m6uOt0qGeKAeVjGu74IKgEH3G8muqzIm1Cxr7X1r5OJeIgpFy4QxTaz+29FHuvlglzmxZcfe+5nk
-CiKxLU3lSCZpq+Kq8/v8kiky6bM+TR8noc2OuRf7JT7JbvN32g0S9l3HuzYQ1VTW8+DiR0jm3hTa
-YVKvJrT1cU/J19IG32PK/yHoWQbgCNWEFVP3Q+V8xaCJmGtzxmjOZd69fwX3se72V6FglcXM6pM6
-vpmumwKjrckWtc7dXpl4fho5frLABaTAgqWjR56M6ly2vGfb5ipN0gTco65F97yLnByn1tUD3AjL
-LhbKXEAz6GfDLuemROoRRRw1ZS0eRWEkG4IupZ0zXWX4Qfkuy5Q/H6MMMSRE7cderVC6xkGbrPAX
-ZcD4XW9boAo0PO7X6oifmPmvTiT6l7Jkdtqr9O3jw2Dv1fkCyC2fg69naQanMVXVz0tv/wQFx1is
-XxYb5dKj6zHbHzMVTdDypVP1y+E9Tmgt2BLdqvLmTZtJ5cUoobqwWsagtQIDAQABo0IwQDAPBgNV
-HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUiQq0OJMa5qvum5EY+fU8PjXQ
-04IwDQYJKoZIhvcNAQEFBQADggIBADKL9p1Kyb4U5YysOMo6CdQbzoaz3evUuii+Eq5FLAR0rBNR
-xVgYZk2C2tXck8An4b58n1KeElb21Zyp9HWc+jcSjxyT7Ff+Bw+r1RL3D65hXlaASfX8MPWbTx9B
-LxyE04nH4toCdu0Jz2zBuByDHBb6lM19oMgY0sidbvW9adRtPTXoHqJPYNcHKfyyo6SdbhWSVhlM
-CrDpfNIZTUJG7L399ldb3Zh+pE3McgODWF3vkzpBemOqfDqo9ayk0d2iLbYq/J8BjuIQscTK5Gfb
-VSUZP/3oNn6z4eGBrxEWi1CXYBmCAMBrTXO40RMHPuq2MU/wQppt4hF05ZSsjYSVPCGvxdpHyN85
-YmLLW1AL14FABZyb7bq2ix4Eb5YgOe2kfSnbSM6C3NQCjR0EMVrHS/BsYVLXtFHCgWzN4funodKS
-ds+xDzdYpPJScWc/DIh4gInByLUfkmO+p3qKViwaqKactV2zY9ATIKHrkWzQjX2v3wvkF7mGnjix
-lAxYjOBVqjtjbZqJYLhkKpLGN/R+Q0O3c+gB53+XD9fyexn9GtePyfqFa3qdnom2piiZk4hA9z7N
-UaPK6u95RyG1/jLix8NRb76AdPCkwzryT+lf3xkK8jsTQ6wxpLPn6/wY1gGp8yqPNg7rtLG8t0zJ
-a7+h89n07eLw4+1knj0vllJPgFOL
------END CERTIFICATE-----
-
CA Disig Root R2
================
-----BEGIN CERTIFICATE-----
@@ -3150,66 +2331,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 +2659,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 +2712,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 +3102,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 1ebad0bca4..977cd2b09c 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",
@@ -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 bee3c886a6..c35cde88e0 100644
--- a/platform/android/CHANGELOG.md
+++ b/platform/android/CHANGELOG.md
@@ -2,9 +2,44 @@
Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started.
+## 6.1.0 - May 4, 2018
+ - Unwrap LatLngBounds during JNI conversion [#11807](https://github.com/mapbox/mapbox-gl-native/pull/11807)
+ - Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800)
+ - Null-check source before removing [#11789](https://github.com/mapbox/mapbox-gl-native/pull/11789)
+ - Flutter support: promote pixel-ratio to public API NativeMapView [#11772](https://github.com/mapbox/mapbox-gl-native/pull/11772)
+ - Unwrap LatLngBounds for the shortest path when requesting camera [#11759](https://github.com/mapbox/mapbox-gl-native/pull/11759)
+ - Flutter support: integrate view callback abstraction [#11706](https://github.com/mapbox/mapbox-gl-native/pull/11706)
+ - Match expression doc tweaks [#11691](https://github.com/mapbox/mapbox-gl-native/pull/11691)
+ - Improve stop javadoc to include interpolate [#11677](https://github.com/mapbox/mapbox-gl-native/pull/11677)
+ - Reduce per-frame render CPU time [#11811](https://github.com/mapbox/mapbox-gl-native/issues/11811)
+ - Add Korean localization [#11792](https://github.com/mapbox/mapbox-gl-native/pull/11792)
+ - Add Danish localization; update Hungarian, Russian, Swedish translations [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11136)
+
## 5.5.3 - May 4, 2018
- Check if renderer is not destroyed before delivering snapshot [#11800](https://github.com/mapbox/mapbox-gl-native/pull/11800)
+## 6.0.1 - April 17, 2018
+ - Bump telemetry version to 3.0.2 [#11710](https://github.com/mapbox/mapbox-gl-native/pull/11710)
+
+## 6.0.0 - April 17, 2018
+ - Bump telemetry version to 3.0.1 [#11700](https://github.com/mapbox/mapbox-gl-native/pull/11700)
+ - Update layer when changing its min/max zoom [#11687](https://github.com/mapbox/mapbox-gl-native/pull/11687)
+
+## 6.0.0-beta.7 - April 12, 2018
+ - Add abs, round, floor, ceil expression operators [#11653](https://github.com/mapbox/mapbox-gl-native/pull/11653)
+ - LatLngBounds correct center calculation [#11650](https://github.com/mapbox/mapbox-gl-native/pull/11650)
+ - Bump telemetry to 3.0.0 final [#11658](https://github.com/mapbox/mapbox-gl-native/pull/11658)
+ - Correctly calculate LatLngBounds [#11647](https://github.com/mapbox/mapbox-gl-native/pull/11647)
+ - Add convenience step expression [#11641](https://github.com/mapbox/mapbox-gl-native/pull/11641)
+ - Add javadoc examples for Android [#11540](https://github.com/mapbox/mapbox-gl-native/pull/11540)
+ - Add paused state to map renderer, don't render snapshots after onPause [#11358](https://github.com/mapbox/mapbox-gl-native/pull/11358)
+ - Rework internal expression conversion [#11490](https://github.com/mapbox/mapbox-gl-native/pull/11490)
+ - Fixed gesture event listeners javadoc [#11630](https://github.com/mapbox/mapbox-gl-native/pull/11630)
+ - Add delete local reference on jni strings [#11608](https://github.com/mapbox/mapbox-gl-native/pull/11608)
+ - Release local references early [#11599](https://github.com/mapbox/mapbox-gl-native/pull/11599)
+ - Re-bind uniform locations after re-linking program [#11618](https://github.com/mapbox/mapbox-gl-native/pull/11618)
+ - Bump mapbox-sdk-services to 3.0.1 [#11593](https://github.com/mapbox/mapbox-gl-native/pull/11593)
+
## 5.5.2 - April 10, 2018
- Correct animation scale point [#11643](https://github.com/mapbox/mapbox-gl-native/pull/11643)
- Re-bind uniform locations after re-linking program [#11583](https://github.com/mapbox/mapbox-gl-native/pull/11583)
@@ -13,6 +48,24 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Add missing delete local reference [#11608](https://github.com/mapbox/mapbox-gl-native/pull/11608)
- Release local refs [#11599](https://github.com/mapbox/mapbox-gl-native/pull/11599)
+## 6.0.0-beta.6 - April 4, 2018
+ - Fix race condition crash for heavily modified annotations [#11551](https://github.com/mapbox/mapbox-gl-native/pull/11551)
+ - Throw exception when converting PropertyValue with an expression [#11572](https://github.com/mapbox/mapbox-gl-native/pull/11572)
+ - Invalidate camera position before delivering onMapReady [#11585](https://github.com/mapbox/mapbox-gl-native/pull/11585)
+ - Rework logical condition convenience expressions [#11555](https://github.com/mapbox/mapbox-gl-native/pull/11555)
+ - Telemetry library version update to 3.0.0-beta.4 [#11590](https://github.com/mapbox/mapbox-gl-native/pull/11590)
+
+## 6.0.0-beta.5 - March 27, 2018
+ - Avoid flashing on pitched overzoomed tiles [#11488](https://github.com/mapbox/mapbox-gl-native/pull/11488)
+ - Literal array conversion of primitive arrays [#11500](https://github.com/mapbox/mapbox-gl-native/pull/11500)
+ - Make default output from step more descriptive [#11501](https://github.com/mapbox/mapbox-gl-native/pull/11501)
+ - Expose public Telemetry API [#11503](https://github.com/mapbox/mapbox-gl-native/pull/11503)
+ - Convert Android int colors with to-color expression [#11506](https://github.com/mapbox/mapbox-gl-native/pull/11506)
+ - Prevent default style reload when string style json was set [#11520](https://github.com/mapbox/mapbox-gl-native/pull/11520)
+ - String, number and bool Expressions with multiple values [#11512](https://github.com/mapbox/mapbox-gl-native/pull/11512)
+ - Telemetry library 3.0.0-beta.3 [#11534](https://github.com/mapbox/mapbox-gl-native/pull/11534)
+ - Gestures library v0.2.0 [#11535](https://github.com/mapbox/mapbox-gl-native/pull/11535)
+
## 5.5.1 - March 26, 2018
- Verify optional access of FileSource deactivation [#11480](https://github.com/mapbox/mapbox-gl-native/pull/11480)
- Prevent default style loading when style json was set [#11519](https://github.com/mapbox/mapbox-gl-native/pull/11519)
@@ -22,6 +75,39 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Null check body of http request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413)
- Clamp TileJSON bounds [#11425](https://github.com/mapbox/mapbox-gl-native/pull/11425)
+## 6.0.0-beta.4 - March 20, 2018
+ - Gesture library 0.1.0 stable [#11483](https://github.com/mapbox/mapbox-gl-native/pull/11483)
+ - Filters with expressions [#11429](https://github.com/mapbox/mapbox-gl-native/pull/11429)
+ - Telemetry 3.0.0-beta.2 [#11474](https://github.com/mapbox/mapbox-gl-native/pull/11474)
+ - High level JNI conversion for geojson [#11471](https://github.com/mapbox/mapbox-gl-native/pull/11471)
+ - Update to MAS 3.0.0-beta.4 [#11468](https://github.com/mapbox/mapbox-gl-native/pull/11468)
+ - Support for expression literal on arrays [#11457](https://github.com/mapbox/mapbox-gl-native/pull/11457)
+ - Fix telemetry integration for two finger tap gesture [#11460](https://github.com/mapbox/mapbox-gl-native/pull/11460)
+ - Revisit proguard configuration [#11434](https://github.com/mapbox/mapbox-gl-native/pull/11434)
+ - Expressions accessor support [#11352](https://github.com/mapbox/mapbox-gl-native/pull/11352)
+ - Calculate camera's LatLng for bounds without map padding [#11410](https://github.com/mapbox/mapbox-gl-native/pull/11410)
+ - Check for null on http body request [#11413](https://github.com/mapbox/mapbox-gl-native/pull/11413)
+ - Expose more gesture settings [#11407](https://github.com/mapbox/mapbox-gl-native/pull/11407)
+ - Revert java 8 language support [#11398](https://github.com/mapbox/mapbox-gl-native/pull/11398)
+ - Update to MAS 3.0.0-beta.3 [#11373](https://github.com/mapbox/mapbox-gl-native/pull/11373)
+ - Rework match expression to style specification syntax [#11388](https://github.com/mapbox/mapbox-gl-native/pull/11388)
+ - Update javadoc configuration for Gradle 4.4 [#11384](https://github.com/mapbox/mapbox-gl-native/pull/11384)
+ - Rework zoomIn and zoomOut to use ValueAnimators [#11382](https://github.com/mapbox/mapbox-gl-native/pull/11382)
+ - Delete LocalRef when converting Image.java [#11350](https://github.com/mapbox/mapbox-gl-native/pull/11350)
+ - Use float for pixelratio when creating a snapshotter [#11367](https://github.com/mapbox/mapbox-gl-native/pull/11367)
+ - Validate width/height when creating a snapshot [#11364](https://github.com/mapbox/mapbox-gl-native/pull/11364)
+
+## 6.0.0-beta.3 - March 2, 2018
+ - Added missing local reference deletes [#11243](https://github.com/mapbox/mapbox-gl-native/pull/11243), [#11272](https://github.com/mapbox/mapbox-gl-native/pull/11272)
+ - Remove obsolete camera api [#11201](https://github.com/mapbox/mapbox-gl-native/pull/11201)
+ - Fix UTF-8 encoding, add missing package-info.java files [#11261](https://github.com/mapbox/mapbox-gl-native/pull/11261)
+ - Rework expression api [#11210](https://github.com/mapbox/mapbox-gl-native/pull/11210)
+ - LatLngBounds fixes [#11333](https://github.com/mapbox/mapbox-gl-native/pull/11333), [#11307](https://github.com/mapbox/mapbox-gl-native/pull/11307), [#11308](https://github.com/mapbox/mapbox-gl-native/pull/11308), [#11309](https://github.com/mapbox/mapbox-gl-native/pull/11309), [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226)
+ - New gestures library [#11221](https://github.com/mapbox/mapbox-gl-native/pull/11221)
+ - Expose ImageSource coordinates setter [#11262](https://github.com/mapbox/mapbox-gl-native/pull/11262)
+ - Add heatmap color property [#11220](https://github.com/mapbox/mapbox-gl-native/pull/11220)
+ - Add support for mapzen terrarium raster-dem encoding [#11339](https://github.com/mapbox/mapbox-gl-native/pull/11339)
+
## 5.5.0 - March 1, 2018
- TileJSON Bounds allows values inclusive of world extents [#11178](https://github.com/mapbox/mapbox-gl-native/pull/11178)
- LatLngBounds returned by VisibleRegion when map is rotated [#11226](https://github.com/mapbox/mapbox-gl-native/pull/11226)
@@ -34,6 +120,16 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to
- Continue loading style even if we mutate it [#11294](https://github.com/mapbox/mapbox-gl-native/pull/11294)
- Update telemetry version for OkHttp [#11338](https://github.com/mapbox/mapbox-gl-native/pull/11338)
+## 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)
@@ -44,7 +140,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
@@ -58,7 +154,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)
@@ -78,6 +184,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
@@ -149,9 +256,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
@@ -164,13 +271,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)
@@ -217,10 +324,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.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
+## 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)
@@ -313,7 +427,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)
@@ -715,12 +829,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
index cec211fe81..c24bd2563a 100644
--- a/platform/android/MapboxGLAndroidSDK/.gitignore
+++ b/platform/android/MapboxGLAndroidSDK/.gitignore
@@ -1,2 +1 @@
-lint-baseline.xml
-lint/lint-baseline-local.xml \ No newline at end of file
+dependency-graph-mapbox-libraries.png
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index f70f987271..c43bc9112b 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -1,37 +1,28 @@
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
- exclude group: 'com.android.support'
- }
+ api dependenciesList.mapboxAndroidTelemetry
+ api dependenciesList.mapboxJavaGeoJSON
+ api dependenciesList.mapboxAndroidGestures
+ implementation dependenciesList.supportAnnotations
+ implementation dependenciesList.supportFragmentV4
+ implementation dependenciesList.timber
+ implementation dependenciesList.okhttp3
+ 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 +62,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"
@@ -120,15 +111,14 @@ android {
}
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 +126,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 +141,8 @@ 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"
+apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle"
diff --git a/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle b/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
deleted file mode 100644
index eeb5e9b4d6..0000000000
--- a/platform/android/MapboxGLAndroidSDK/gradle-javadoc.gradle
+++ /dev/null
@@ -1,21 +0,0 @@
-android.libraryVariants.all { variant ->
- def name = variant.name
- // noinspection GroovyAssignabilityCheck
- task "javadoc$name"(type: Javadoc) {
- description = "Generates javadoc for build $name"
- failOnError = false
- destinationDir = new File(destinationDir, variant.baseName)
- source = files(variant.javaCompile.source)
- classpath = files(variant.javaCompile.classpath.files) + files(android.bootClasspath)
- 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.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")
- options.group("Mapbox Android SDK", "com.mapbox.*")
- options.group("Third Party Libraries", "com.almeros.*")
- exclude '**/R.java', '**/BuildConfig.java', 'com/almeros/**'
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties
index 5b8260a53c..84aeabef1a 100644
--- a/platform/android/MapboxGLAndroidSDK/gradle.properties
+++ b/platform/android/MapboxGLAndroidSDK/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.mapbox.mapboxsdk
-VERSION_NAME=5.6.0-SNAPSHOT
+VERSION_NAME=7.0.0-SNAPSHOT
POM_DESCRIPTION=Mapbox GL Android SDK
POM_URL=https://github.com/mapbox/mapbox-gl-native
@@ -18,4 +18,3 @@ POM_PACKAGING=aar
# Only build native dependencies for the current ABI
# See https://code.google.com/p/android/issues/detail?id=221098#c20
android.buildOnlyTargetAbi=true
-
diff --git a/platform/android/MapboxGLAndroidSDK/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/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
index 3b8adac5a8..83bd7df835 100644
--- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
+++ b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro
@@ -2,17 +2,42 @@
# in ../sdk/tools/proguard/proguard-android.txt,
# contents of this file will be appended into proguard-android.txt
-keepattributes Signature, *Annotation*, EnclosingMethod
--keep class almeros.android.multitouch.gesturedetectors.** { *; }
+
+# config for the SDK
-keep class com.mapbox.mapboxsdk.** { *; }
-keep interface com.mapbox.mapboxsdk.** { *; }
--keep class com.mapbox.services.android.telemetry.** { *; }
--keep class com.mapbox.services.commons.** { *;}
--keep class com.google.gson.** { *; }
# config for okhttp 3.8.0, https://github.com/square/okhttp/pull/3354
-dontwarn okio.**
--dontwarn javax.annotation.Nullable
--dontwarn javax.annotation.ParametersAreNonnullByDefault
+-dontwarn javax.annotation.**
+-dontnote okhttp3.**
+
+# config for mapbox-android-telemetry:3.0.0-beta.1
+-keep class com.mapbox.android.telemetry.** { *; }
+-dontwarn com.mapbox.android.core.location.MockLocationEngine
+-dontwarn com.mapbox.android.core.location.MockLocationEngine$LocationUpdateRunnable
+-dontwarn java.awt.Color
+-dontwarn com.mapzen.android.lost.api**
+-dontwarn org.conscrypt.OpenSSLProvider
+-dontwarn org.conscrypt.Conscrypt
+
+# config for mapbox-sdk-geojson:3.0.1
+-keep class com.mapbox.geojson.** { *; }
+-keep class com.google.gson.** { *; }
+-dontnote com.google.gson.internal.UnsafeAllocator
+
+# config for mapbox-android-gestures:0.0.1-20180228.152340-13
+-dontnote com.mapbox.android.gestures.*
-# config for optional location provider https://github.com/mapbox/mapbox-gl-native/issues/10960
--dontwarn com.mapzen.android.lost.api** \ No newline at end of file
+# config for additional warnings
+-keep class com.google.android.gms.dynamite.descriptors.com.google.android.gms.flags.ModuleDescriptor { java.lang.String MODULE_ID; }
+-keep class com.google.android.gms.dynamite.descriptors.com.google.android.gms.flags.ModuleDescriptor { int MODULE_VERSION; }
+-keep class com.google.android.gms.dynamite.DynamiteModule$DynamiteLoaderClassLoader { java.lang.ClassLoader sClassLoader; }
+-dontnote com.google.devtools.build.android.desugar.runtime.ThrowableExtension
+-dontnote org.robolectric.Robolectric
+-dontnote libcore.io.Memory
+-dontnote com.google.protobuf.ExtensionRegistry
+-dontnote com.google.protobuf.Extension
+-dontnote android.net.**
+-dontnote org.apache.http.**
+-dontwarn com.sun.xml.internal.ws.spi.db.** \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
index f59585bfe5..dcbec69bfc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml
@@ -1,20 +1,15 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
package="com.mapbox.mapboxsdk">
- <uses-feature android:glEsVersion="0x00020000" android:required="true" />
- <uses-feature android:name="android.hardware.wifi" android:required="false" /> <!-- Implied by ACCESS_WIFI_STATE. -->
- <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true"/>
+ <uses-feature
+ android:name="android.hardware.wifi"
+ android:required="false"/> <!-- Implied by ACCESS_WIFI_STATE. -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <uses-sdk tools:overrideLibrary="com.mapzen.lost"/>
-
- <application>
- <!-- Include the telemetry service to simplify set up (https://www.mapbox.com/telemetry) -->
- <service android:name="com.mapbox.services.android.telemetry.service.TelemetryService"/>
- </application>
-
</manifest>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java
deleted file mode 100644
index b7bcb925a1..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/BaseGestureDetector.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public abstract class BaseGestureDetector {
- protected final Context context;
- protected boolean gestureInProgress;
-
- protected MotionEvent prevEvent;
- protected MotionEvent currEvent;
-
- protected float currPressure;
- protected float prevPressure;
- protected long timeDelta;
-
- /**
- * This value is the threshold ratio between the previous combined pressure
- * and the current combined pressure. When pressure decreases rapidly
- * between events the position values can often be imprecise, as it usually
- * indicates that the user is in the process of lifting a pointer off of the
- * device. This value was tuned experimentally.
- */
- protected static final float PRESSURE_THRESHOLD = 0.67f;
-
- public BaseGestureDetector(Context context) {
- this.context = context;
- }
-
- /**
- * All gesture detectors need to be called through this method to be able to
- * detect gestures. This method delegates work to handler methods
- * (handleStartProgressEvent, handleInProgressEvent) implemented in
- * extending classes.
- *
- * @param event MotionEvent
- * @return {@code true} as handled
- */
- public boolean onTouchEvent(MotionEvent event) {
- final int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
- if (!gestureInProgress) {
- handleStartProgressEvent(actionCode, event);
- } else {
- handleInProgressEvent(actionCode, event);
- }
- return true;
- }
-
- /**
- * Called when the current event occurred when NO gesture is in progress
- * yet. The handling in this implementation may set the gesture in progress
- * (via gestureInProgress) or out of progress
- *
- * @param actionCode Action Code from MotionEvent
- * @param event MotionEvent
- */
- protected abstract void handleStartProgressEvent(int actionCode,
- MotionEvent event);
-
- /**
- * Called when the current event occurred when a gesture IS in progress. The
- * handling in this implementation may set the gesture out of progress (via
- * gestureInProgress).
- *
- * @param actionCode Action Code from MotionEvent
- * @param event MotionEvent
- */
- protected abstract void handleInProgressEvent(int actionCode,
- MotionEvent event);
-
- protected void updateStateByEvent(MotionEvent curr) {
- final MotionEvent prev = prevEvent;
-
- // Reset currEvent
- if (currEvent != null) {
- currEvent.recycle();
- currEvent = null;
- }
- currEvent = MotionEvent.obtain(curr);
-
- // Delta time
- timeDelta = curr.getEventTime() - prev.getEventTime();
-
- // Pressure
- currPressure = curr.getPressure(curr.getActionIndex());
- prevPressure = prev.getPressure(prev.getActionIndex());
- }
-
- protected void resetState() {
- if (prevEvent != null) {
- prevEvent.recycle();
- prevEvent = null;
- }
- if (currEvent != null) {
- currEvent.recycle();
- currEvent = null;
- }
- gestureInProgress = false;
- }
-
- /**
- * Returns {@code true} if a gesture is currently in progress.
- *
- * @return {@code true} if a gesture is currently in progress, {@code false}
- * otherwise.
- */
- public boolean isInProgress() {
- return gestureInProgress;
- }
-
- /**
- * Return the time difference in milliseconds between the previous accepted
- * GestureDetector event and the current GestureDetector event.
- *
- * @return Time difference since the last move event in milliseconds.
- */
- public long getTimeDelta() {
- return timeDelta;
- }
-
- /**
- * Return the event time of the current GestureDetector event being
- * processed.
- *
- * @return Current GestureDetector event time in milliseconds.
- */
- public long getEventTime() {
- return currEvent.getEventTime();
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java
deleted file mode 100644
index bc7dda6159..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/MoveGestureDetector.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class MoveGestureDetector extends BaseGestureDetector {
-
- /**
- * Listener which must be implemented which is used by MoveGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * MoveGestureDetector via the constructor.
- *
- * @see MoveGestureDetector.SimpleOnMoveGestureListener
- */
- public interface OnMoveGestureListener {
- public boolean onMove(MoveGestureDetector detector);
-
- public boolean onMoveBegin(MoveGestureDetector detector);
-
- public void onMoveEnd(MoveGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnMoveGestureListener.
- */
- public static class SimpleOnMoveGestureListener implements
- OnMoveGestureListener {
- public boolean onMove(MoveGestureDetector detector) {
- return false;
- }
-
- public boolean onMoveBegin(MoveGestureDetector detector) {
- return true;
- }
-
- public void onMoveEnd(MoveGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private static final PointF FOCUS_DELTA_ZERO = new PointF();
-
- private final OnMoveGestureListener listener;
-
- private PointF focusExternal = new PointF();
- private PointF focusDeltaExternal = new PointF();
-
- public MoveGestureDetector(Context context, OnMoveGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_DOWN:
- resetState(); // In case we missed an UP/CANCEL event
-
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
- break;
-
- case MotionEvent.ACTION_MOVE:
- gestureInProgress = listener.onMoveBegin(this);
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- listener.onMoveEnd(this);
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = listener.onMove(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
-
- // Focus intenal
- PointF currFocusInternal = determineFocalPoint(curr);
- PointF prevFocusInternal = determineFocalPoint(prev);
-
- // Focus external
- // - Prevent skipping of focus delta when a finger is added or removed
- boolean skipNextMoveEvent = prev.getPointerCount() != curr
- .getPointerCount();
- focusDeltaExternal = skipNextMoveEvent ? FOCUS_DELTA_ZERO
- : new PointF(currFocusInternal.x - prevFocusInternal.x,
- currFocusInternal.y - prevFocusInternal.y);
-
- // - Don't directly use mFocusInternal (or skipping will occur). Add
- // unskipped delta values to focusExternal instead.
- focusExternal.x += focusDeltaExternal.x;
- focusExternal.y += focusDeltaExternal.y;
- }
-
- /**
- * Determine (multi)finger focal point (a.k.a. center point between all
- * fingers)
- *
- * @param motionEvent a {@link MotionEvent} object.
- * @return PointF focal point
- */
- private PointF determineFocalPoint(MotionEvent motionEvent) {
- // Number of fingers on screen
- final int pCount = motionEvent.getPointerCount();
- float x = 0.0f;
- float y = 0.0f;
-
- for (int i = 0; i < pCount; i++) {
- x += motionEvent.getX(i);
- y += motionEvent.getY(i);
- }
-
- return new PointF(x / pCount, y / pCount);
- }
-
- public float getFocusX() {
- return focusExternal.x;
- }
-
- public float getFocusY() {
- return focusExternal.y;
- }
-
- public PointF getFocusDelta() {
- return focusDeltaExternal;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java
deleted file mode 100644
index 8c111a68df..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/RotateGestureDetector.java
+++ /dev/null
@@ -1,180 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class RotateGestureDetector extends TwoFingerGestureDetector {
-
- /**
- * Listener which must be implemented which is used by RotateGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * RotateGestureDetector via the constructor.
- *
- * @see RotateGestureDetector.SimpleOnRotateGestureListener
- */
- public interface OnRotateGestureListener {
- public boolean onRotate(RotateGestureDetector detector);
-
- public boolean onRotateBegin(RotateGestureDetector detector);
-
- public void onRotateEnd(RotateGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnRotateGestureListener.
- */
- public static class SimpleOnRotateGestureListener implements
- OnRotateGestureListener {
- public boolean onRotate(RotateGestureDetector detector) {
- return false;
- }
-
- public boolean onRotateBegin(RotateGestureDetector detector) {
- return true;
- }
-
- public void onRotateEnd(RotateGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private final OnRotateGestureListener listener;
- private boolean sloppyGesture;
-
- public RotateGestureDetector(Context context,
- OnRotateGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_DOWN:
- // At least the second finger is on screen now
-
- resetState(); // In case we missed an UP/CANCEL event
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
-
- // See if we have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start gesture now
- gestureInProgress = listener.onRotateBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!sloppyGesture) {
- break;
- }
-
- // See if we still have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start normal gesture now
- gestureInProgress = listener.onRotateBegin(this);
- }
-
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (!sloppyGesture) {
- break;
- }
-
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_UP:
- // Gesture ended but
- updateStateByEvent(event);
-
- if (!sloppyGesture) {
- listener.onRotateEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (!sloppyGesture) {
- listener.onRotateEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD) {
- final boolean updatePrevious = listener.onRotate(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- @Override
- protected void resetState() {
- super.resetState();
- sloppyGesture = false;
- }
-
- /**
- * Return the rotation difference from the previous rotate event to the
- * current event.
- *
- * @return The current rotation //difference in degrees.
- */
- public float getRotationDegreesDelta() {
- double diffRadians = Math.atan2(prevFingerDiffY, prevFingerDiffX)
- - Math.atan2(currFingerDiffY, currFingerDiffX);
- return (float) (diffRadians * 180.0 / Math.PI);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java
deleted file mode 100644
index 9396578e48..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/ShoveGestureDetector.java
+++ /dev/null
@@ -1,214 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.view.MotionEvent;
-
-/**
- * @author Robert Nordan (robert.nordan@norkart.no)
- * <p>
- * Copyright (c) 2013, Norkart AS
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public class ShoveGestureDetector extends TwoFingerGestureDetector {
-
- /**
- * Listener which must be implemented which is used by ShoveGestureDetector
- * to perform callbacks to any implementing class which is registered to a
- * ShoveGestureDetector via the constructor.
- *
- * @see ShoveGestureDetector.SimpleOnShoveGestureListener
- */
- public interface OnShoveGestureListener {
- public boolean onShove(ShoveGestureDetector detector);
-
- public boolean onShoveBegin(ShoveGestureDetector detector);
-
- public void onShoveEnd(ShoveGestureDetector detector);
- }
-
- /**
- * Helper class which may be extended and where the methods may be
- * implemented. This way it is not necessary to implement all methods of
- * OnShoveGestureListener.
- */
- public static class SimpleOnShoveGestureListener implements
- OnShoveGestureListener {
- public boolean onShove(ShoveGestureDetector detector) {
- return false;
- }
-
- public boolean onShoveBegin(ShoveGestureDetector detector) {
- return true;
- }
-
- public void onShoveEnd(ShoveGestureDetector detector) {
- // Do nothing, overridden implementation may be used
- }
- }
-
- private float prevAverageY;
- private float currAverageY;
-
- private final OnShoveGestureListener listener;
- private boolean sloppyGesture;
-
- public ShoveGestureDetector(Context context, OnShoveGestureListener listener) {
- super(context);
- this.listener = listener;
- }
-
- @Override
- protected void handleStartProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_DOWN:
- // At least the second finger is on screen now
-
- resetState(); // In case we missed an UP/CANCEL event
- prevEvent = MotionEvent.obtain(event);
- timeDelta = 0;
-
- updateStateByEvent(event);
-
- // See if we have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start gesture now
- gestureInProgress = listener.onShoveBegin(this);
- }
- break;
-
- case MotionEvent.ACTION_MOVE:
- if (!sloppyGesture) {
- break;
- }
-
- // See if we still have a sloppy gesture
- sloppyGesture = isSloppyGesture(event);
- if (!sloppyGesture) {
- // No, start normal gesture now
- gestureInProgress = listener.onShoveBegin(this);
- }
-
- break;
-
- case MotionEvent.ACTION_POINTER_UP:
- if (!sloppyGesture) {
- break;
- }
-
- break;
- }
- }
-
- @Override
- protected void handleInProgressEvent(int actionCode, MotionEvent event) {
- switch (actionCode) {
- case MotionEvent.ACTION_POINTER_UP:
- // Gesture ended but
- updateStateByEvent(event);
-
- if (!sloppyGesture) {
- listener.onShoveEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- if (!sloppyGesture) {
- listener.onShoveEnd(this);
- }
-
- resetState();
- break;
-
- case MotionEvent.ACTION_MOVE:
- updateStateByEvent(event);
-
- // Only accept the event if our relative pressure is within
- // a certain limit. This can help filter shaky data as a
- // finger is lifted. Also check that shove is meaningful.
- if (currPressure / prevPressure > PRESSURE_THRESHOLD
- && Math.abs(getShovePixelsDelta()) > 0.5f) {
- final boolean updatePrevious = listener.onShove(this);
- if (updatePrevious) {
- prevEvent.recycle();
- prevEvent = MotionEvent.obtain(event);
- }
- }
- break;
- }
- }
-
- @Override
- protected void resetState() {
- super.resetState();
- sloppyGesture = false;
- prevAverageY = 0.0f;
- currAverageY = 0.0f;
- }
-
- @Override
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
- float py0 = prev.getY(0);
- float py1 = prev.getY(1);
- prevAverageY = (py0 + py1) / 2.0f;
-
- float cy0 = curr.getY(0);
- float cy1 = curr.getY(1);
- currAverageY = (cy0 + cy1) / 2.0f;
- }
-
- @Override
- protected boolean isSloppyGesture(MotionEvent event) {
- boolean sloppy = super.isSloppyGesture(event);
- if (sloppy) {
- return true;
- }
-
- // If it's not traditionally sloppy, we check if the angle between
- // fingers
- // is acceptable.
- double angle = Math.abs(Math.atan2(currFingerDiffY, currFingerDiffX));
- // about 20 degrees, left or right
- return !((0.0f < angle && angle < 0.35f) || 2.79f < angle
- && angle < Math.PI);
- }
-
- /**
- * Return the distance in pixels from the previous shove event to the
- * current event.
- *
- * @return The current distance in pixels.
- */
- public float getShovePixelsDelta() {
- return currAverageY - prevAverageY;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
deleted file mode 100644
index db492b6556..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/TwoFingerGestureDetector.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package com.almeros.android.multitouch.gesturedetectors;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-/**
- * @author Almer Thie (code.almeros.com) Copyright (c) 2013, Almer Thie
- * (code.almeros.com)
- * <p>
- * All rights reserved.
- * <p>
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * <p>
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * <p>
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-public abstract class TwoFingerGestureDetector extends BaseGestureDetector {
-
- private final float edgeSlop;
-
- protected float prevFingerDiffX;
- protected float prevFingerDiffY;
- protected float currFingerDiffX;
- protected float currFingerDiffY;
-
- private float currLen;
- private float prevLen;
-
- private PointF focus;
-
- public TwoFingerGestureDetector(Context context) {
- super(context);
-
- ViewConfiguration config = ViewConfiguration.get(context);
-
- edgeSlop = config.getScaledEdgeSlop();
- }
-
- @Override
- protected abstract void handleStartProgressEvent(int actionCode,
- MotionEvent event);
-
- @Override
- protected abstract void handleInProgressEvent(int actionCode,
- MotionEvent event);
-
- protected void updateStateByEvent(MotionEvent curr) {
- super.updateStateByEvent(curr);
-
- final MotionEvent prev = prevEvent;
-
- currLen = -1;
- prevLen = -1;
-
- // Previous
- final float px0 = prev.getX(0);
- final float py0 = prev.getY(0);
- final float px1 = prev.getX(1);
- final float py1 = prev.getY(1);
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- prevFingerDiffX = pvx;
- prevFingerDiffY = pvy;
-
- // Current
- final float cx0 = curr.getX(0);
- final float cy0 = curr.getY(0);
- final float cx1 = curr.getX(1);
- final float cy1 = curr.getY(1);
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- currFingerDiffX = cvx;
- currFingerDiffY = cvy;
- focus = determineFocalPoint(curr);
- }
-
- /**
- * Return the current distance between the two pointers forming the gesture
- * in progress.
- *
- * @return Distance between pointers in pixels.
- */
- public float getCurrentSpan() {
- if (currLen == -1) {
- final float cvx = currFingerDiffX;
- final float cvy = currFingerDiffY;
- currLen = (float) Math.sqrt(cvx * cvx + cvy * cvy);
- }
- return currLen;
- }
-
- /**
- * Return the previous distance between the two pointers forming the gesture
- * in progress.
- *
- * @return Previous distance between pointers in pixels.
- */
- public float getPreviousSpan() {
- if (prevLen == -1) {
- final float pvx = prevFingerDiffX;
- final float pvy = prevFingerDiffY;
- prevLen = (float) Math.sqrt(pvx * pvx + pvy * pvy);
- }
- return prevLen;
- }
-
- /**
- * MotionEvent has no getRawX(int) method; simulate it pending future API
- * approval.
- *
- * @param event Motion Event
- * @param pointerIndex Pointer Index
- * @return Raw x value or 0
- */
- protected static float getRawX(MotionEvent event, int pointerIndex) {
- float offset = event.getRawX() - event.getX();
- if (pointerIndex < event.getPointerCount()) {
- return event.getX(pointerIndex) + offset;
- }
- return 0.0f;
- }
-
- /**
- * MotionEvent has no getRawY(int) method; simulate it pending future API
- * approval.
- *
- * @param event Motion Event
- * @param pointerIndex Pointer Index
- * @return Raw y value or 0
- */
- protected static float getRawY(MotionEvent event, int pointerIndex) {
- float offset = event.getRawY() - event.getY();
- if (pointerIndex < event.getPointerCount()) {
- return event.getY(pointerIndex) + offset;
- }
- return 0.0f;
- }
-
- /**
- * Check if we have a sloppy gesture. Sloppy gestures can happen if the edge
- * of the user's hand is touching the screen, for example.
- *
- * @param event Motion Event
- * @return {@code true} if is sloppy gesture, {@code false} if not
- */
- protected boolean isSloppyGesture(MotionEvent event) {
- // As orientation can change, query the metrics in touch down
- DisplayMetrics metrics = context.getResources().getDisplayMetrics();
- float rightSlopEdge = metrics.widthPixels - edgeSlop;
- float bottomSlopEdge = metrics.heightPixels - edgeSlop;
-
- final float edgeSlop = this.edgeSlop;
-
- final float x0 = event.getRawX();
- final float y0 = event.getRawY();
- final float x1 = getRawX(event, 1);
- final float y1 = getRawY(event, 1);
-
- boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop || x0 > rightSlopEdge
- || y0 > bottomSlopEdge;
- boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop || x1 > rightSlopEdge
- || y1 > bottomSlopEdge;
-
- if (p0sloppy && p1sloppy) {
- return true;
- } else if (p0sloppy) {
- return true;
- } else if (p1sloppy) {
- return true;
- }
- return false;
- }
-
- /**
- * Determine (multi)finger focal point (a.k.a. center point between all
- * fingers)
- *
- * @param motionEvent Motion Event
- * @return PointF focal point
- */
- public static PointF determineFocalPoint(MotionEvent motionEvent) {
- // Number of fingers on screen
- final int pCount = motionEvent.getPointerCount();
- float x = 0.0f;
- float y = 0.0f;
-
- for (int i = 0; i < pCount; i++) {
- x += motionEvent.getX(i);
- y += motionEvent.getY(i);
- }
-
- return new PointF(x / pCount, y / pCount);
- }
-
- public float getFocusX() {
- return focus.x;
- }
-
- public float getFocusY() {
- return focus.y;
- }
-
-} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java
deleted file mode 100644
index cff2f086dc..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/almeros/android/multitouch/gesturedetectors/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * Do not use this package. Internal use only.
- */
-package com.almeros.android.multitouch.gesturedetectors;
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..858c1eed67 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java
@@ -10,14 +10,8 @@ import android.text.TextUtils;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.mapboxsdk.location.LocationSource;
+import com.mapbox.mapboxsdk.maps.Telemetry;
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.
@@ -35,7 +29,6 @@ public final class Mapbox {
private Context context;
private String accessToken;
private Boolean connected;
- private LocationEngine locationEngine;
/**
* Get an instance of Mapbox.
@@ -51,27 +44,18 @@ public final class Mapbox {
public static synchronized Mapbox getInstance(@NonNull Context context, @NonNull String accessToken) {
if (INSTANCE == null) {
Context appContext = context.getApplicationContext();
- LocationEngineProvider locationEngineProvider = new LocationEngineProvider(context);
- LocationEngine locationEngine = locationEngineProvider.obtainBestLocationEngineAvailable();
- 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");
- }
+ INSTANCE = new Mapbox(appContext, accessToken);
+ Telemetry.initialize();
ConnectivityReceiver.instance(appContext);
}
+
return INSTANCE;
}
- Mapbox(@NonNull Context context, @NonNull String accessToken, LocationEngine locationEngine) {
+ Mapbox(@NonNull Context context, @NonNull String accessToken) {
this.context = context;
this.accessToken = accessToken;
- this.locationEngine = locationEngine;
}
/**
@@ -144,25 +128,4 @@ public final class Mapbox {
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
return (activeNetwork != null && activeNetwork.isConnected());
}
-
- /**
- * Returns a location source instance with empty methods.
- *
- * @return an empty location source implementation
- * @deprecated Replaced by {@link Mapbox#getLocationEngine()}
- */
- @Deprecated
- public static LocationSource getLocationSource() {
- return new EmptyLocationSource();
- }
-
-
- /**
- * Returns the location engine used by the SDK.
- *
- * @return the location engine configured
- */
- public static LocationEngine getLocationEngine() {
- return INSTANCE.locationEngine;
- }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java
new file mode 100644
index 0000000000..57d840af52
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android Attribution API classes.
+ */
+package com.mapbox.mapboxsdk.attribution;
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/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
index 50e33f4f9f..8ef0d5b523 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java
@@ -148,6 +148,26 @@ public final class CameraUpdateFactory {
return new ZoomUpdate(ZoomUpdate.ZOOM_TO, zoom);
}
+ /**
+ * Returns a CameraUpdate that moves the camera viewpoint to a particular bearing.
+ *
+ * @param bearing Bearing to change to
+ * @return CameraUpdate Final Camera Position
+ */
+ public static CameraUpdate bearingTo(double bearing) {
+ return new CameraPositionUpdate(bearing, null, -1, -1);
+ }
+
+ /**
+ * Returns a CameraUpdate that moves the camera viewpoint to a particular tilt.
+ *
+ * @param tilt Tilt to change to
+ * @return CameraUpdate Final Camera Position
+ */
+ public static CameraUpdate tiltTo(double tilt) {
+ return new CameraPositionUpdate(-1, null, tilt, -1);
+ }
+
//
// CameraUpdate types
//
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/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
index 60362dd2e9..640c70282c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java
@@ -48,6 +48,31 @@ public class MapboxConstants {
public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000;
/**
+ * Value by which the default rotation threshold will be increased when scaling
+ */
+ public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f;
+
+ /**
+ * Time within which user needs to lift fingers for velocity animation to start.
+ */
+ public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L;
+
+ /**
+ * Minimum angular velocity for rotation animation
+ */
+ public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f;
+
+ /**
+ * Maximum angular velocity for rotation animation
+ */
+ public static final float MAXIMUM_ANGULAR_VELOCITY = 20f;
+
+ /**
+ * Factor to calculate tilt change based on pixel change during shove gesture.
+ */
+ public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f;
+
+ /**
* The currently supported minimum zoom level.
*/
public static final float MINIMUM_ZOOM = 0.0f;
@@ -78,14 +103,14 @@ public class MapboxConstants {
public static final double MINIMUM_DIRECTION = 0;
/**
- * The currently used minimun scale factor to clamp to when a quick zoom gesture occurs
+ * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs
*/
public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f;
/**
* The currently used maximum scale factor to clamp to when a quick zoom gesture occurs
*/
- public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.45f;
+ public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f;
/**
* Fragment Argument Key for MapboxMapOptions
@@ -96,23 +121,13 @@ public class MapboxConstants {
public static final String STATE_HAS_SAVED_STATE = "mapbox_savedState";
public static final String STATE_CAMERA_POSITION = "mapbox_cameraPosition";
public static final String STATE_ZOOM_ENABLED = "mapbox_zoomEnabled";
- public static final String STATE_ZOOM_ENABLED_CHANGE = "mapbox_zoomEnabledChange";
public static final String STATE_SCROLL_ENABLED = "mapbox_scrollEnabled";
- public static final String STATE_SCROLL_ENABLED_CHANGE = "mapbox_scrollEnabledChange";
public static final String STATE_ROTATE_ENABLED = "mapbox_rotateEnabled";
- public static final String STATE_ROTATE_ENABLED_CHANGE = "mapbox_rotateEnabledChange";
public static final String STATE_TILT_ENABLED = "mapbox_tiltEnabled";
- public static final String STATE_TILT_ENABLED_CHANGE = "mapbox_tiltEnabledChange";
public static final String STATE_ZOOM_CONTROLS_ENABLED = "mapbox_zoomControlsEnabled";
public static final String STATE_DOUBLE_TAP_ENABLED = "mapbox_doubleTapEnabled";
- public static final String STATE_DOUBLE_TAP_ENABLED_CHANGE = "mapbox_doubleTapEnabledChange";
public static final String STATE_DEBUG_ACTIVE = "mapbox_debugActive";
public static final String STATE_STYLE_URL = "mapbox_styleUrl";
- public static final String STATE_MY_LOCATION_ENABLED = "mapbox_myLocationEnabled";
- public static final String STATE_MY_LOCATION_TRACKING_MODE = "mapbox_myLocationTracking";
- public static final String STATE_MY_BEARING_TRACKING_MODE = "mapbox_myBearingTracking";
- public static final String STATE_MY_LOCATION_TRACKING_DISMISS = "mapbox_myLocationTrackingDismiss";
- public static final String STATE_MY_BEARING_TRACKING_DISMISS = "mapbox_myBearingTrackingDismiss";
public static final String STATE_COMPASS_ENABLED = "mapbox_compassEnabled";
public static final String STATE_COMPASS_GRAVITY = "mapbox_compassGravity";
public static final String STATE_COMPASS_MARGIN_LEFT = "mapbox_compassMarginLeft";
@@ -133,20 +148,12 @@ public class MapboxConstants {
public static final String STATE_ATTRIBUTION_MARGIN_RIGHT = "mapbox_attrMarginRight";
public static final String STATE_ATTRIBUTION_MARGIN_BOTTOM = "mapbox_atrrMarginBottom";
public static final String STATE_ATTRIBUTION_ENABLED = "mapbox_atrrEnabled";
- public static final String STATE_LOCATION_CHANGE_ANIMATION_ENABLED = "mapbox_locationChangeAnimationEnabled";
- public static final String STATE_USING_CUSTOM_LOCATION_SOURCE = "mapbox_usingCustomLocationSource";
- public static final String STATE_LOCATION_VIEW_ENABLED = "mapbox_locViewEnabled";
- public static final String STATE_LOCATION_VIEW_FOREGROUND_DRAWABLE = "mapbox_locViewForegroundDrawable";
- public static final String STATE_LOCATION_VIEW_FOREGROUND_BEARING_DRAWABLE = "mapbox_locViewBearingDrawable";
- public static final String STATE_LOCATION_VIEW_FOREGROUND_TINT_COLOR = "mapbox_locViewForegroundTintColor";
- public static final String STATE_LOCATION_VIEW_BACKGROUND_DRAWABLE = "mapbox_locViewBackgroundDrawable";
- public static final String STATE_LOCATION_VIEW_BACKGROUND_OFFSET = "mapbox_locViewBackgroundOffset";
- public static final String STATE_LOCATION_VIEW_BACKGROUND_TINT_COLOR = "mapbox_locViewBackgroundTintColor";
- public static final String STATE_LOCATION_VIEW_ACCURACY_ALPHA = "mapbox_locViewAccuracyAlpha";
- public static final String STATE_LOCATION_VIEW_ACCURACY_TINT_COLOR = "mapbox_locViewAccuracyTintColor";
- public static final String STATE_LOCATION_VIEW_ACCURACY_THRESHOLD = "mapbox_locViewAccuracyThreshold";
- public static final String STATE_LOCATION_VIEW_PADDING = "mapbox_locViewPadding";
public static final String STATE_DESELECT_MARKER_ON_TAP = "mapbox_deselectMarkerOnTap";
public static final String STATE_USER_FOCAL_POINT = "mapbox_userFocalPoint";
+ public static final String STATE_SCALE_ANIMATION_ENABLED = "mapbox_scaleAnimationEnabled";
+ public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled";
+ public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled";
+ public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold";
+ public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold";
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
deleted file mode 100644
index c042b00577..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyBearingTracking.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.mapbox.mapboxsdk.constants;
-
-import android.support.annotation.IntDef;
-
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * MyBearingTracking exposes different types of bearing tracking modes.
- * <p>
- * These modes visualise the user direction by extracting the direction from either sensor or location data.
- * </p>
- * <p>
- * Required to enable showing the user location first through {@link MapboxMap#setMyLocationEnabled(boolean)}.
- * </p>
- *
- * @see com.mapbox.mapboxsdk.maps.TrackingSettings#setMyBearingTrackingMode(int)
- * @see MyLocationView#setMyBearingTrackingMode(int)
- */
-public class MyBearingTracking {
-
- @IntDef( {NONE, COMPASS, GPS, GPS_NORTH_FACING})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Mode {
- }
-
- /**
- * Bearing tracking is disabled
- */
- public static final int NONE = 0x00000000;
-
- /**
- * Tracking the bearing of the user based on sensor data
- */
- public static final int COMPASS = 0x00000004;
-
- /**
- * Tracking the bearing of the user based on GPS data
- */
- public static final int GPS = 0x00000008;
-
- /**
- * Tracking the bearing of the user based on GPS data, but camera always faces north direction
- */
- public static final int GPS_NORTH_FACING = 0x0000000B;
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
deleted file mode 100644
index 1283283fa5..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MyLocationTracking.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.mapbox.mapboxsdk.constants;
-
-import android.support.annotation.IntDef;
-
-import com.mapbox.mapboxsdk.maps.MapView;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * MyLocationTracking exposes types of location tracking modes.
- * * <p>
- * This allows tracking the user location on screen by updating the camera position when a location update occurs.
- * </p>
- * <p>
- * Required to enable showing the user location first through {@link MapboxMap#setMyLocationEnabled(boolean)}.
- * </p>
- *
- * @see MapboxMap#setMyLocationEnabled(boolean)
- * @see TrackingSettings#setMyLocationTrackingMode(int)
- */
-public class MyLocationTracking {
-
- @IntDef( {TRACKING_NONE, TRACKING_FOLLOW})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Mode {
- }
-
- /**
- * Tracking the location of the user is disabled.
- */
- public static final int TRACKING_NONE = 0x00000000;
-
- /**
- * Tracking the location of the user. {@link MapView} will reposition to center of {@link MyLocationView}
- */
- public static final int TRACKING_FOLLOW = 0x00000004;
-
-}
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..90cb56f605 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;
@@ -27,6 +28,10 @@ public class LatLngBounds implements Parcelable {
/**
* Construct a new LatLngBounds based on its corners, given in NESW
* order.
+ * <p>
+ * 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
@@ -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,20 @@ 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);
}
/**
@@ -162,9 +178,24 @@ 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 +225,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);
}
/**
@@ -224,13 +278,73 @@ public class LatLngBounds implements Parcelable {
/**
* Constructs a LatLngBounds from doubles representing a LatLng pair.
* <p>
+ * 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.
+ * latNorth should be greater or equal latSouth, 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");
+ }
+
+ if (latNorth < latSouth) {
+ throw new IllegalArgumentException("LatSouth cannot be less than latNorth");
+ }
+
+ 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.
+ * <p>
+ * Returned bounds will have latitude in the range of Mercator projection.
+ *
+ * @param z Tile zoom level.
+ * @param x Tile X coordinate.
+ * @param y Tile Y coordinate.
+ * @see GeometryConstants#MIN_MERCATOR_LATITUDE
+ * @see GeometryConstants#MAX_MERCATOR_LATITUDE
+ */
+ 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 +380,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 +405,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 +416,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 +434,28 @@ 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) {
+ double north = (this.latitudeNorth < latNorth) ? latNorth : this.latitudeNorth;
+ double south = (this.latitudeSouth > latSouth) ? latSouth : this.latitudeSouth;
+
+ if (LatLngSpan.getLongitudeSpan(lonEast, this.longitudeWest)
+ < LatLngSpan.getLongitudeSpan(this.longitudeEast, lonWest)) {
+ return new LatLngBounds(north,
+ lonEast,
+ south,
+ this.longitudeWest);
+ }
+
+ return new LatLngBounds(north,
+ this.longitudeEast,
+ south,
+ lonWest);
}
/**
@@ -326,13 +466,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/geometry/LatLngSpan.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java
index 322c7dfb74..133949f743 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java
@@ -4,6 +4,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
+import com.mapbox.mapboxsdk.constants.GeometryConstants;
+
/**
* A geographical span defined by its latitude and longitude span.
*/
@@ -136,4 +138,20 @@ public class LatLngSpan implements Parcelable {
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
+
+ /**
+ * Get the absolute distance, in degrees, between the west and
+ * east boundaries of this LatLngBounds
+ *
+ * @return Span distance
+ */
+ static double getLongitudeSpan(double east, double west) {
+ double longSpan = Math.abs(east - west);
+ if (east > west) {
+ return longSpan;
+ }
+
+ // shortest span contains antimeridian
+ return GeometryConstants.LONGITUDE_SPAN - longSpan;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
index 3491ec5c85..d0e51f941f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HTTPRequest.java
@@ -7,10 +7,10 @@ import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.util.Log;
+import com.mapbox.android.telemetry.TelemetryUtils;
import com.mapbox.mapboxsdk.BuildConfig;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.services.android.telemetry.utils.TelemetryUtils;
import java.io.IOException;
import java.io.InterruptedIOException;
@@ -29,7 +29,6 @@ import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-
import okhttp3.ResponseBody;
import timber.log.Timber;
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 48de768e64..39cd25631e 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java
@@ -11,12 +11,13 @@ import android.support.annotation.NonNull;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Toast;
+
+import com.mapbox.android.telemetry.TelemetryEnabler;
import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.attribution.Attribution;
import com.mapbox.mapboxsdk.attribution.AttributionParser;
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;
@@ -31,17 +32,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;
}
@@ -59,12 +59,11 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface.
// check is hosting activity isn't finishing
// https://github.com/mapbox/mapbox-gl-native/issues/11238
if (!isActivityFinishing) {
- 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);
@@ -90,7 +89,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() {
@@ -100,7 +99,8 @@ 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);
+ TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.ENABLED);
+ Telemetry.obtainTelemetry().enable();
dialog.cancel();
}
});
@@ -114,7 +114,8 @@ 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);
+ Telemetry.obtainTelemetry().disable();
+ TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.DISABLED);
dialog.cancel();
}
});
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
index 07106689be..de9b4fdbc2 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java
@@ -5,27 +5,32 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.PointF;
-import android.location.Location;
+import android.os.Handler;
import android.support.annotation.Nullable;
-import android.support.v4.view.GestureDetectorCompat;
-import android.support.v4.view.ScaleGestureDetectorCompat;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.view.InputDevice;
import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.VelocityTracker;
-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 android.view.animation.DecelerateInterpolator;
+
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.Constants;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.MultiFingerTapGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import com.mapbox.android.telemetry.Event;
+import com.mapbox.android.telemetry.MapEventFactory;
+import com.mapbox.android.telemetry.MapState;
+import com.mapbox.mapboxsdk.R;
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.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION;
@@ -33,24 +38,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE
/**
* Manages gestures events on a MapView.
- * <p>
- * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors.
- * </p>
*/
final class MapGestureDetector {
private final Transform transform;
private final Projection projection;
private final UiSettings uiSettings;
- private final TrackingSettings trackingSettings;
private final AnnotationManager annotationManager;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private GestureDetectorCompat gestureDetector;
- private ScaleGestureDetector scaleGestureDetector;
- private RotateGestureDetector rotateGestureDetector;
- private ShoveGestureDetector shoveGestureDetector;
-
// deprecated map touch API
private MapboxMap.OnMapClickListener onMapClickListener;
private MapboxMap.OnMapLongClickListener onMapLongClickListener;
@@ -70,44 +66,96 @@ final class MapGestureDetector {
private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList
= new CopyOnWriteArrayList<>();
- private PointF focalPoint;
+ private final CopyOnWriteArrayList<MapboxMap.OnMoveListener> onMoveListenerList
+ = new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<MapboxMap.OnRotateListener> onRotateListenerList
+ = new CopyOnWriteArrayList<>();
+
+ private final CopyOnWriteArrayList<MapboxMap.OnScaleListener> onScaleListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean twoTap;
- private boolean quickZoom;
- private boolean tiltGestureOccurred;
- private boolean scrollGestureOccurred;
+ private final CopyOnWriteArrayList<MapboxMap.OnShoveListener> onShoveListenerList
+ = new CopyOnWriteArrayList<>();
- private boolean scaleGestureOccurred;
- private boolean recentScaleGestureOccurred;
- private long scaleBeginTime;
+ /**
+ * User-set focal point.
+ */
+ private PointF focalPoint;
+
+ private AndroidGesturesManager gesturesManager;
+ private boolean executeDoubleTap;
- private boolean scaleAnimating;
- private boolean rotateAnimating;
+ private Animator scaleAnimator;
+ private Animator rotateAnimator;
+ private final List<Animator> scheduledAnimators = new ArrayList<>();
- private VelocityTracker velocityTracker;
- private boolean wasZoomingIn;
- private boolean wasClockwiseRotating;
- private boolean rotateGestureOccurred;
+ /**
+ * Cancels scheduled velocity animations if user doesn't lift fingers within
+ * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT}
+ */
+ private Handler animationsTimeoutHandler = new Handler();
MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings,
- TrackingSettings trackingSettings, AnnotationManager annotationManager,
- CameraChangeDispatcher cameraChangeDispatcher) {
+ AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) {
this.annotationManager = annotationManager;
this.transform = transform;
this.projection = projection;
this.uiSettings = uiSettings;
- this.trackingSettings = trackingSettings;
this.cameraChangeDispatcher = cameraChangeDispatcher;
- // Touch gesture detectors
+ // Checking for context != null for testing purposes
if (context != null) {
- gestureDetector = new GestureDetectorCompat(context, new GestureListener());
- gestureDetector.setIsLongpressEnabled(true);
- scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener());
- ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true);
- rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener());
- shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener());
+ // Initialize gestures manager
+ AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context);
+ initializeGesturesManager(androidGesturesManager, true);
+
+ // Initialize gesture listeners
+ initializeGestureListeners(context, true);
+ }
+ }
+
+ private void initializeGestureListeners(Context context, boolean attachDefaultListeners) {
+ if (attachDefaultListeners) {
+ StandardGestureListener standardGestureListener = new StandardGestureListener();
+ MoveGestureListener moveGestureListener = new MoveGestureListener();
+ ScaleGestureListener scaleGestureListener = new ScaleGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity));
+ RotateGestureListener rotateGestureListener = new RotateGestureListener(
+ context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating),
+ context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity),
+ context.getResources().getDimension(R.dimen.mapbox_defaultScaleSpanSinceStartThreshold));
+ ShoveGestureListener shoveGestureListener = new ShoveGestureListener();
+ TapGestureListener tapGestureListener = new TapGestureListener();
+
+ gesturesManager.setStandardGestureListener(standardGestureListener);
+ gesturesManager.setMoveGestureListener(moveGestureListener);
+ gesturesManager.setStandardScaleGestureListener(scaleGestureListener);
+ gesturesManager.setRotateGestureListener(rotateGestureListener);
+ gesturesManager.setShoveGestureListener(shoveGestureListener);
+ gesturesManager.setMultiFingerTapGestureListener(tapGestureListener);
+ }
+ }
+
+ private void initializeGesturesManager(AndroidGesturesManager androidGesturesManager,
+ boolean setDefaultMutuallyExclusives) {
+ if (setDefaultMutuallyExclusives) {
+ Set<Integer> shoveScaleSet = new HashSet<>();
+ shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
+ shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
+
+ Set<Integer> shoveRotateSet = new HashSet<>();
+ shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE);
+ shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE);
+
+ Set<Integer> ScaleLongPressSet = new HashSet<>();
+ ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE);
+ ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS);
+
+ androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet);
}
+
+ gesturesManager = androidGesturesManager;
}
/**
@@ -133,8 +181,9 @@ final class MapGestureDetector {
/**
* Get the current active gesture focal point.
* <p>
- * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point
- * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}.
+ * This could be either the user provided focal point in
+ * {@link UiSettings#setFocalPoint(PointF)}or <code>null</code>.
+ * If it's <code>null</code>, gestures will use focal pointed returned by the detector.
* </p>
*
* @return the current active gesture focal point.
@@ -145,128 +194,86 @@ 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.
* </p>
*
- * @param event the MotionEvent
+ * @param motionEvent the MotionEvent
* @return True if touch event is handled
*/
- boolean onTouchEvent(MotionEvent event) {
- // framework can return null motion events in edge cases #9432
- if (event == null) {
+ boolean onTouchEvent(MotionEvent motionEvent) {
+ // Framework can return null motion events in edge cases #9432
+ if (motionEvent == null) {
return false;
}
// Check and ignore non touch or left clicks
- if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
+ if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) {
return false;
}
- // Check two finger gestures first
- scaleGestureDetector.onTouchEvent(event);
- rotateGestureDetector.onTouchEvent(event);
- shoveGestureDetector.onTouchEvent(event);
+ boolean result = gesturesManager.onTouchEvent(motionEvent);
- // Handle two finger tap
- switch (event.getActionMasked()) {
+ switch (motionEvent.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- if (velocityTracker == null) {
- velocityTracker = VelocityTracker.obtain();
- } else {
- velocityTracker.clear();
- }
- velocityTracker.addMovement(event);
- // First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666
- recentScaleGestureOccurred = false;
+ cancelAnimators();
transform.setGestureInProgress(true);
break;
+ case MotionEvent.ACTION_UP:
+ transform.setGestureInProgress(false);
- case MotionEvent.ACTION_POINTER_DOWN:
- // Second pointer down
- twoTap = event.getPointerCount() == 2
- && uiSettings.isZoomGesturesEnabled();
- if (twoTap) {
- // Confirmed 2nd Finger Down
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(event.getX(), event.getY()),
- MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform));
+ // Start all awaiting velocity animations
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ for (Animator animator : scheduledAnimators) {
+ animator.start();
}
+ scheduledAnimators.clear();
break;
- case MotionEvent.ACTION_POINTER_UP:
- // Second pointer up
+ case MotionEvent.ACTION_CANCEL:
+ scheduledAnimators.clear();
+ transform.setGestureInProgress(false);
break;
+ }
- case MotionEvent.ACTION_UP:
- // First pointer up
- long tapInterval = event.getEventTime() - event.getDownTime();
- boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout();
- boolean inProgress = rotateGestureDetector.isInProgress()
- || scaleGestureDetector.isInProgress()
- || shoveGestureDetector.isInProgress();
-
- if (twoTap && isTap && !inProgress) {
- if (focalPoint != null) {
- transform.zoom(false, focalPoint);
- } else {
- PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event);
- transform.zoom(false, focalPoint);
- }
- twoTap = false;
- return true;
- }
+ return result;
+ }
- // Scroll / Pan Has Stopped
- if (scrollGestureOccurred) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent(
- getLocationFromGesture(event.getX(), event.getY()), transform));
- scrollGestureOccurred = false;
+ void cancelAnimators() {
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ scheduledAnimators.clear();
- if (!scaleAnimating && !rotateAnimating) {
- cameraChangeDispatcher.onCameraIdle();
- }
- }
+ cancelAnimator(scaleAnimator);
+ cancelAnimator(rotateAnimator);
+ }
- twoTap = false;
- transform.setGestureInProgress(false);
- if (velocityTracker != null) {
- velocityTracker.recycle();
- }
- velocityTracker = null;
- break;
+ private void cancelAnimator(Animator animator) {
+ if (animator != null && animator.isStarted()) {
+ animator.cancel();
+ }
+ }
- case MotionEvent.ACTION_CANCEL:
- twoTap = false;
- transform.setGestureInProgress(false);
- if (velocityTracker != null) {
- velocityTracker.recycle();
- }
- velocityTracker = null;
- break;
- case MotionEvent.ACTION_MOVE:
- if (velocityTracker != null) {
- velocityTracker.addMovement(event);
- velocityTracker.computeCurrentVelocity(1000);
- }
- break;
+ /**
+ * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed.
+ */
+ private Runnable cancelAnimatorsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cancelAnimators();
}
+ };
- return gestureDetector.onTouchEvent(event);
+ /**
+ * Schedules a velocity animator to be executed when user lift fingers,
+ * unless canceled by the {@link #cancelAnimatorsRunnable}.
+ *
+ * @param animator animator ot be scheduled
+ */
+ private void scheduleAnimator(Animator animator) {
+ scheduledAnimators.add(animator);
+ animationsTimeoutHandler.removeCallbacksAndMessages(null);
+ animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT);
}
/**
@@ -275,7 +282,7 @@ final class MapGestureDetector {
* Examples of such events are mouse scroll events, mouse moves, joystick & trackpad.
* </p>
*
- * @param event The MotionEvent occured
+ * @param event The MotionEvent occurred
* @return True is the event is handled
*/
boolean onGenericMotionEvent(MotionEvent event) {
@@ -297,7 +304,7 @@ final class MapGestureDetector {
float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
// Scale the map by the appropriate power of two factor
- transform.zoomBy(scrollDist, event.getX(), event.getY());
+ transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY()));
return true;
@@ -311,57 +318,14 @@ final class MapGestureDetector {
return false;
}
- /**
- * Responsible for handling one finger gestures.
- */
- private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener {
-
+ private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener {
@Override
- public boolean onDown(MotionEvent event) {
- return true;
- }
-
- @Override
- public boolean onDoubleTapEvent(MotionEvent e) {
- if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) {
- return false;
- }
-
- switch (e.getAction()) {
- case MotionEvent.ACTION_DOWN:
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- if (quickZoom) {
- cameraChangeDispatcher.onCameraIdle();
- quickZoom = false;
- break;
- }
-
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
-
- // Single finger double tap
- if (focalPoint != null) {
- // User provided focal point
- transform.zoom(true, focalPoint);
- } else {
- // 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));
- break;
- }
-
+ public boolean onDown(MotionEvent motionEvent) {
return true;
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
- // Cancel any animation
transform.cancelTransitions();
return true;
}
@@ -380,27 +344,60 @@ final class MapGestureDetector {
notifyOnMapClickListeners(tapPoint);
}
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(motionEvent.getX(), motionEvent.getY()),
- MapboxEvent.GESTURE_SINGLETAP, transform));
+ sendTelemetryEvent(Telemetry.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
return true;
}
@Override
- public void onLongPress(MotionEvent motionEvent) {
- PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ public boolean onDoubleTapEvent(MotionEvent motionEvent) {
+ int action = motionEvent.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ executeDoubleTap = true;
+ }
+ if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) {
+ return false;
+ }
+
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ PointF zoomFocalPoint;
+ // Single finger double tap
+ if (focalPoint != null) {
+ // User provided focal point
+ zoomFocalPoint = focalPoint;
+ } else {
+ // Zoom in on gesture
+ zoomFocalPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ }
- if (!quickZoom) {
- notifyOnMapLongClickListeners(longClickPoint);
+ zoomInAnimated(zoomFocalPoint, false);
+
+ sendTelemetryEvent(Telemetry.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY()));
+
+ return true;
}
+ return super.onDoubleTapEvent(motionEvent);
+ }
+
+ @Override
+ public void onLongPress(MotionEvent motionEvent) {
+ PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY());
+ notifyOnMapLongClickListeners(longClickPoint);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) {
- // don't allow a fling is scroll is disabled
- // and ignore when a scale gesture has occurred
+ if (!uiSettings.isScrollGesturesEnabled()) {
+ // don't allow a fling if scroll is disabled
+ return false;
+ }
+
+ notifyOnFlingListeners();
+
+ if (!uiSettings.isFlingVelocityAnimationEnabled()) {
return false;
}
@@ -413,11 +410,7 @@ final class MapGestureDetector {
return false;
}
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
-
- // cancel any animation
transform.cancelTransitions();
-
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
// tilt results in a bigger translation, limiting input for #5281
@@ -432,487 +425,600 @@ final class MapGestureDetector {
// update transformation
transform.moveBy(offsetX, offsetY, animationTime);
- notifyOnFlingListeners();
return true;
}
+ }
- // Called for drags
+ private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener {
@Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ public boolean onMoveBegin(MoveGestureDetector detector) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
- if (tiltGestureOccurred) {
- return false;
- }
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- if (!scrollGestureOccurred) {
- scrollGestureOccurred = true;
+ sendTelemetryEvent(Telemetry.PAN, detector.getFocalPoint());
- // Cancel any animation
- if (!scaleGestureOccurred) {
- transform.cancelTransitions();
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- }
+ notifyOnMoveBeginListeners(detector);
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent(
- getLocationFromGesture(e1.getX(), e1.getY()),
- MapboxEvent.GESTURE_PAN_START, transform));
- }
+ return true;
+ }
- // reset tracking if needed
- trackingSettings.resetTrackingModesIfRequired(true, false, false);
+ @Override
+ public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
// Scroll the map
transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
notifyOnScrollListeners();
+ notifyOnMoveListeners(detector);
return true;
}
- }
-
- void notifyOnMapClickListeners(PointF tapPoint) {
- // deprecated API
- if (onMapClickListener != null) {
- onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint));
- }
- // new API
- for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) {
- listener.onMapClick(projection.fromScreenLocation(tapPoint));
+ @Override
+ public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
+ notifyOnMoveEndListeners(detector);
}
}
- void notifyOnMapLongClickListeners(PointF longClickPoint) {
- // deprecated API
- if (onMapLongClickListener != null) {
- onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
+ private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener {
- // new API
- for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
- listener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
- }
- }
+ private final float minimumVelocity;
- void notifyOnFlingListeners() {
- // deprecated API
- if (onFlingListener != null) {
- onFlingListener.onFling();
- }
+ private PointF scaleFocalPoint;
+ private boolean quickZoom;
- // new API
- for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
- listener.onFling();
+ ScaleGestureListener(float minimumVelocity) {
+ this.minimumVelocity = minimumVelocity;
}
- }
- void notifyOnScrollListeners() {
- //deprecated API
- if (onScrollListener != null) {
- onScrollListener.onScroll();
- }
+ @Override
+ public boolean onScaleBegin(StandardScaleGestureDetector detector) {
+ if (!uiSettings.isZoomGesturesEnabled()) {
+ return false;
+ }
- // new API
- for (MapboxMap.OnScrollListener listener : onScrollListenerList) {
- listener.onScroll();
- }
- }
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- /**
- * Responsible for handling two finger gestures and double-tap drag gestures.
- */
- private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
+ quickZoom = detector.getPointersCount() == 1;
+ if (quickZoom) {
+ // when quickzoom, dismiss double tap and disable move gesture
+ executeDoubleTap = false;
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
+ }
+
+ if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
+ // increase rotate angle threshold when scale is detected first
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
+ + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING
+ );
+ }
+
+ // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd()
+ setScaleFocalPoint(detector);
- private static final int ANIMATION_TIME_MULTIPLIER = 77;
- private static final double ZOOM_DISTANCE_DIVIDER = 5;
+ sendTelemetryEvent(Telemetry.PINCH, scaleFocalPoint);
- private float scaleFactor = 1.0f;
- private PointF scalePointBegin;
+ notifyOnScaleBeginListeners(detector);
+
+ return true;
+ }
- // Called when two fingers first touch the screen
@Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (!uiSettings.isZoomGesturesEnabled()) {
- return false;
- }
+ public boolean onScale(StandardScaleGestureDetector detector) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
+
+ setScaleFocalPoint(detector);
+
+ float scaleFactor = detector.getScaleFactor();
+ double zoomBy = getNewZoom(scaleFactor, quickZoom);
+ transform.zoomBy(zoomBy, scaleFocalPoint);
+
+ notifyOnScaleListeners(detector);
- 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));
return true;
}
- // Called each time a finger moves
- // Called for pinch zooms and quickzooms/quickscales
@Override
- public boolean onScale(ScaleGestureDetector detector) {
- if (!uiSettings.isZoomGesturesEnabled()) {
- return super.onScale(detector);
- }
+ public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
- wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0;
- if (tiltGestureOccurred) {
- return false;
+ if (quickZoom) {
+ //if quickzoom, re-enabling move gesture detector
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
}
- // Ignore short touches in case it is a tap
- // Also ignore small scales
- long time = detector.getEventTime();
- long interval = time - scaleBeginTime;
- if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- return false;
+ if (uiSettings.isIncreaseRotateThresholdWhenScaling()) {
+ // resetting default angle threshold
+ gesturesManager.getRotateGestureDetector().setAngleThreshold(
+ Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD
+ );
}
- // If scale is large enough ignore a tap
- scaleFactor *= detector.getScaleFactor();
- if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) {
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- scaleGestureOccurred = true;
- }
+ notifyOnScaleEndListeners(detector);
- if (!scaleGestureOccurred) {
- return false;
+ if (!uiSettings.isScaleVelocityAnimationEnabled()) {
+ return;
}
- // Gesture is a quickzoom if there aren't two fingers
- if (!quickZoom && !twoTap) {
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+ float velocityXY = Math.abs(velocityX) + Math.abs(velocityY);
+ if (velocityXY > minimumVelocity) {
+ double zoomAddition = calculateScale(velocityXY, detector.isScalingOut());
+ double currentZoom = transform.getRawZoom();
+ long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4);
+ scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, scaleFocalPoint, animationTime);
+ scheduleAnimator(scaleAnimator);
}
- quickZoom = !twoTap;
+ }
- // make an assumption here; if the zoom center is specified by the gesture, it's NOT going
- // to be in the center of the map. Therefore the zoom will translate the map center, so tracking
- // should be disabled.
- trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false);
- // Scale the map
+ private void setScaleFocalPoint(StandardScaleGestureDetector detector) {
if (focalPoint != null) {
- // arround user provided focal point
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y);
+ // around user provided focal point
+ scaleFocalPoint = focalPoint;
} else if (quickZoom) {
- cameraChangeDispatcher.onCameraMove();
+ // around center
+ scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
+ } else {
+ // around gesture
+ scaleFocalPoint = detector.getFocalPoint();
+ }
+ }
+
+ private double calculateScale(double velocityXY, boolean isScalingOut) {
+ double zoomAddition = (float) Math.log(velocityXY / 1000 + 1);
+ if (isScalingOut) {
+ zoomAddition = -zoomAddition;
+ }
+ return zoomAddition;
+ }
+
+ private double getNewZoom(float scaleFactor, boolean quickZoom) {
+ double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
+ if (quickZoom) {
// clamp scale factors we feed to core #7514
- float scaleFactor = detector.getScaleFactor();
- // around center map
- double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2);
boolean negative = zoomBy < 0;
zoomBy = MathUtils.clamp(Math.abs(zoomBy),
MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP,
MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP);
- transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- recentScaleGestureOccurred = true;
- } else {
- // around gesture
- transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2),
- scalePointBegin.x, scalePointBegin.y);
+ return negative ? -zoomBy : zoomBy;
}
+ return zoomBy;
+ }
+ }
+
+ private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
+ private PointF rotateFocalPoint;
+ private final float minimumScaleSpanWhenRotating;
+ private final float minimumAngularVelocity;
+ private final float defaultSpanSinceStartThreshold;
+
+ public RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity,
+ float defaultSpanSinceStartThreshold) {
+ this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating;
+ this.minimumAngularVelocity = minimumAngularVelocity;
+ this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold;
+ }
+
+ @Override
+ public boolean onRotateBegin(RotateGestureDetector detector) {
+ if (!uiSettings.isRotateGesturesEnabled()) {
+ return false;
+ }
+
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
+
+ if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
+ // when rotation starts, interrupting scale and increasing the threshold
+ // to make rotation without scaling easier
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating);
+ gesturesManager.getStandardScaleGestureDetector().interrupt();
+ }
+
+ // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd()
+ setRotateFocalPoint(detector);
+
+ sendTelemetryEvent(Telemetry.ROTATION, rotateFocalPoint);
+
+ notifyOnRotateBeginListeners(detector);
+
+ return true;
+ }
+
+ @Override
+ public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast,
+ float rotationDegreesSinceFirst) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
+
+ setRotateFocalPoint(detector);
+
+ // Calculate map bearing value
+ double bearing = transform.getRawBearing() + rotationDegreesSinceLast;
+
+ // Rotate the map
+ transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y);
+
+ notifyOnRotateListeners(detector);
+
return true;
}
- // Called when fingers leave screen
@Override
- public void onScaleEnd(final ScaleGestureDetector detector) {
- if (velocityTracker == null) {
+ public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) {
+ cameraChangeDispatcher.onCameraIdle();
+
+ if (uiSettings.isIncreaseScaleThresholdWhenRotating()) {
+ // resetting default scale threshold values
+ gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold);
+ }
+
+ notifyOnRotateEndListeners(detector);
+
+ if (!uiSettings.isRotateVelocityAnimationEnabled()) {
return;
}
- if (rotateGestureOccurred || quickZoom) {
- reset();
+ if (Math.abs(angularVelocity) < minimumAngularVelocity) {
return;
}
- double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity());
- if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) {
- scaleAnimating = true;
- double zoomAddition = calculateScale(velocityXY);
- double currentZoom = transform.getRawZoom();
- long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER);
- createScaleAnimator(currentZoom, zoomAddition, animationTime).start();
- } else if (!scaleAnimating) {
- reset();
+ boolean negative = angularVelocity < 0;
+ angularVelocity = (float) Math.pow(angularVelocity, 2);
+ angularVelocity = MathUtils.clamp(
+ angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY);
+
+ long animationTime = (long) (Math.log(angularVelocity + 1) * 500);
+
+ if (negative) {
+ angularVelocity = -angularVelocity;
}
- }
- private void reset() {
- scaleAnimating = false;
- scaleGestureOccurred = false;
- scaleBeginTime = 0;
- scaleFactor = 1.0f;
- cameraChangeDispatcher.onCameraIdle();
+ rotateAnimator = createRotateAnimator(angularVelocity, animationTime);
+ scheduleAnimator(rotateAnimator);
}
- private double calculateScale(double velocityXY) {
- double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER);
- if (!wasZoomingIn) {
- zoomAddition = -zoomAddition;
+ private void setRotateFocalPoint(RotateGestureDetector detector) {
+ if (focalPoint != null) {
+ // User provided focal point
+ rotateFocalPoint = focalPoint;
+ } else {
+ // around gesture
+ rotateFocalPoint = detector.getFocalPoint();
}
- return zoomAddition;
}
- private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) {
- ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
+ private Animator createRotateAnimator(float angularVelocity, long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f);
animator.setDuration(animationTime);
- animator.setInterpolator(new FastOutSlowInInterpolator());
+ animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- PointF scalePoint = focalPoint != null ? focalPoint : scalePointBegin;
- transform.setZoom((Float) animation.getAnimatedValue(), scalePoint, 0, true);
+ transform.setBearing(
+ transform.getRawBearing() + (float) animation.getAnimatedValue(),
+ rotateFocalPoint.x, rotateFocalPoint.y,
+ 0L
+ );
}
});
+
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
@Override
public void onAnimationCancel(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
@Override
public void onAnimationEnd(Animator animation) {
- reset();
+ cameraChangeDispatcher.onCameraIdle();
}
});
+
return animator;
}
}
- /**
- * Responsible for handling rotation gestures.
- */
- private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
-
- private static final float ROTATE_INVOKE_ANGLE = 15.30f;
- private static final float ROTATE_LIMITATION_ANGLE = 3.35f;
- private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f;
-
- private long beginTime = 0;
- private boolean started = false;
-
- // Called when two fingers first touch the screen
+ private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener {
@Override
- public boolean onRotateBegin(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled()) {
+ public boolean onShoveBegin(ShoveGestureDetector detector) {
+ if (!uiSettings.isTiltGesturesEnabled()) {
return false;
}
- // notify camera change listener
+ transform.cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- beginTime = detector.getEventTime();
+ sendTelemetryEvent(Telemetry.PITCH, detector.getFocalPoint());
+
+ // disabling move gesture during shove
+ gesturesManager.getMoveGestureDetector().setEnabled(false);
+
+ notifyOnShoveBeginListeners(detector);
+
return true;
}
- // Called each time one of the two fingers moves
- // Called for rotation
@Override
- public boolean onRotate(RotateGestureDetector detector) {
- if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) {
- return false;
- }
+ public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) {
+ // dispatching start even once more if another detector ended, and this one didn't
+ cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE);
- // If rotate is large enough ignore a tap
- // 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));
- started = true;
- }
+ // Get tilt value (scale and clamp)
+ double pitch = transform.getTilt();
+ pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast;
+ pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT);
- if (!started) {
- return false;
- }
+ // Tilt the map
+ transform.setTilt(pitch);
+
+ notifyOnShoveListeners(detector);
- wasClockwiseRotating = detector.getRotationDegreesDelta() > 0;
- if (scaleBeginTime != 0) {
- rotateGestureOccurred = true;
+ return true;
+ }
+
+ @Override
+ public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) {
+ cameraChangeDispatcher.onCameraIdle();
+
+ // re-enabling move gesture
+ gesturesManager.getMoveGestureDetector().setEnabled(true);
+
+ notifyOnShoveEndListeners(detector);
+ }
+ }
+
+ private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener {
+ @Override
+ public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) {
+ if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) {
+ return false;
}
- // rotation constitutes translation of anything except the center of
- // rotation, so cancel both location and bearing tracking if required
- trackingSettings.resetTrackingModesIfRequired(true, true, false);
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- // Calculate map bearing value
- double bearing = transform.getRawBearing() + angle;
+ sendTelemetryEvent(Telemetry.TWO_FINGER_TAP, detector.getFocalPoint());
- // Rotate the map
+ PointF zoomFocalPoint;
+ // Single finger double tap
if (focalPoint != null) {
// User provided focal point
- transform.setBearing(bearing, focalPoint.x, focalPoint.y);
+ zoomFocalPoint = focalPoint;
} else {
- // around gesture
- transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY());
+ // Zoom in on gesture
+ zoomFocalPoint = detector.getFocalPoint();
}
+
+ zoomOutAnimated(zoomFocalPoint, false);
+
return true;
}
+ }
- // Called when the fingers leave the screen
- @Override
- public void onRotateEnd(RotateGestureDetector detector) {
- long interval = detector.getEventTime() - beginTime;
- if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) {
- reset();
- return;
+ private Animator createScaleAnimator(double currentZoom, double zoomAddition, final PointF animationFocalPoint,
+ long animationTime) {
+ ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition));
+ animator.setDuration(animationTime);
+ animator.setInterpolator(new DecelerateInterpolator());
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ transform.setZoom((Float) animation.getAnimatedValue(), animationFocalPoint);
}
+ });
+
+ animator.addListener(new AnimatorListenerAdapter() {
- double angularVelocity = calculateVelocityVector(detector);
- if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) {
- animateRotateVelocity();
- } else if (!rotateAnimating) {
- reset();
+ @Override
+ public void onAnimationStart(Animator animation) {
+ transform.cancelTransitions();
+ cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
}
- }
- private void reset() {
- beginTime = 0;
- started = false;
- rotateAnimating = false;
- rotateGestureOccurred = false;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ transform.cancelTransitions();
+ }
- if (!twoTap) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
cameraChangeDispatcher.onCameraIdle();
}
+ });
+ return animator;
+ }
+
+ /**
+ * Zoom in by 1.
+ *
+ * @param zoomFocalPoint focal point of zoom animation
+ * @param runImmediately if true, animation will be started right away, otherwise it will wait until
+ * {@link MotionEvent#ACTION_UP} is registered.
+ */
+ void zoomInAnimated(PointF zoomFocalPoint, boolean runImmediately) {
+ zoomAnimated(true, zoomFocalPoint, runImmediately);
+ }
+
+ /**
+ * Zoom out by 1.
+ *
+ * @param zoomFocalPoint focal point of zoom animation
+ * @param runImmediately if true, animation will be started right away, otherwise it will wait until
+ * {@link MotionEvent#ACTION_UP} is registered.
+ */
+ void zoomOutAnimated(PointF zoomFocalPoint, boolean runImmediately) {
+ zoomAnimated(false, zoomFocalPoint, runImmediately);
+ }
+
+ private void zoomAnimated(boolean zoomIn, PointF zoomFocalPoint, boolean runImmediately) {
+ //canceling here as well, because when using a button it will not be canceled automatically by onDown()
+ cancelAnimator(scaleAnimator);
+
+ double currentZoom = transform.getRawZoom();
+ scaleAnimator = createScaleAnimator(
+ currentZoom,
+ zoomIn ? 1 : -1,
+ zoomFocalPoint,
+ MapboxConstants.ANIMATION_DURATION);
+ if (runImmediately) {
+ scaleAnimator.start();
+ } else {
+ scheduleAnimator(scaleAnimator);
}
+ }
- private void animateRotateVelocity() {
- rotateAnimating = true;
- double currentRotation = transform.getRawBearing();
- double rotateAdditionDegrees = calculateVelocityInDegrees();
- createAnimator(currentRotation, rotateAdditionDegrees).start();
+ private void sendTelemetryEvent(String eventType, PointF focalPoint) {
+ if (isZoomValid(transform)) {
+ MapEventFactory mapEventFactory = new MapEventFactory();
+ LatLng latLng = projection.fromScreenLocation(focalPoint);
+ MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom());
+ state.setGesture(eventType);
+ Telemetry.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state));
}
+ }
- private double calculateVelocityVector(RotateGestureDetector detector) {
- return ((detector.getFocusX() * velocityTracker.getYVelocity())
- + (detector.getFocusY() * velocityTracker.getXVelocity()))
- / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2));
+ private boolean isZoomValid(Transform transform) {
+ if (transform == null) {
+ return false;
}
+ double mapZoom = transform.getZoom();
+ return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM;
+ }
- private double calculateVelocityInDegrees() {
- double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity());
- double angle = angleRadians / (Math.PI / 180);
- if (angle <= 0) {
- angle += 360;
- }
+ void notifyOnMapClickListeners(PointF tapPoint) {
+ // deprecated API
+ if (onMapClickListener != null) {
+ onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint));
+ }
- // limit the angle
- angle = angle / ROTATE_LIMITATION_ANGLE;
+ // new API
+ for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) {
+ listener.onMapClick(projection.fromScreenLocation(tapPoint));
+ }
+ }
- // correct direction
- if (!wasClockwiseRotating) {
- angle = -angle;
- }
+ void notifyOnMapLongClickListeners(PointF longClickPoint) {
+ // deprecated API
+ if (onMapLongClickListener != null) {
+ onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
+ }
- return angle;
+ // new API
+ for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) {
+ listener.onMapLongClick(projection.fromScreenLocation(longClickPoint));
}
+ }
- private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) {
- ValueAnimator animator = ValueAnimator.ofFloat(
- (float) currentRotation,
- (float) (currentRotation + rotateAdditionDegrees)
- );
- animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION));
- animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- transform.setBearing((Float) animation.getAnimatedValue());
- }
- });
- animator.addListener(new AnimatorListenerAdapter() {
+ void notifyOnFlingListeners() {
+ // deprecated API
+ if (onFlingListener != null) {
+ onFlingListener.onFling();
+ }
- @Override
- public void onAnimationStart(Animator animation) {
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION);
- }
+ // new API
+ for (MapboxMap.OnFlingListener listener : onFlingListenerList) {
+ listener.onFling();
+ }
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- reset();
- }
+ void notifyOnScrollListeners() {
+ //deprecated API
+ if (onScrollListener != null) {
+ onScrollListener.onScroll();
+ }
- @Override
- public void onAnimationEnd(Animator animation) {
- reset();
- }
- });
- return animator;
+ // new API
+ for (MapboxMap.OnScrollListener listener : onScrollListenerList) {
+ listener.onScroll();
}
}
- /**
- * Responsible for handling 2 finger shove gestures.
- */
- private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener {
+ void notifyOnMoveBeginListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveBegin(detector);
+ }
+ }
- private long beginTime = 0;
- private float totalDelta = 0.0f;
+ void notifyOnMoveListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMove(detector);
+ }
+ }
- @Override
- public boolean onShoveBegin(ShoveGestureDetector detector) {
- if (!uiSettings.isTiltGesturesEnabled()) {
- return false;
- }
+ void notifyOnMoveEndListeners(MoveGestureDetector detector) {
+ for (MapboxMap.OnMoveListener listener : onMoveListenerList) {
+ listener.onMoveEnd(detector);
+ }
+ }
- // notify camera change listener
- cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE);
- return true;
+ void notifyOnRotateBeginListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateBegin(detector);
}
+ }
- @Override
- public void onShoveEnd(ShoveGestureDetector detector) {
- beginTime = 0;
- totalDelta = 0.0f;
- tiltGestureOccurred = false;
+ void notifyOnRotateListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotate(detector);
}
+ }
- @Override
- public boolean onShove(ShoveGestureDetector detector) {
- if (!uiSettings.isTiltGesturesEnabled()) {
- return false;
- }
+ void notifyOnRotateEndListeners(RotateGestureDetector detector) {
+ for (MapboxMap.OnRotateListener listener : onRotateListenerList) {
+ listener.onRotateEnd(detector);
+ }
+ }
- // Ignore short touches in case it is a tap
- // Also ignore small tilt
- long time = detector.getEventTime();
- long interval = time - beginTime;
- if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) {
- return false;
- }
+ void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleBegin(detector);
+ }
+ }
- // If tilt is large enough ignore a tap
- // Also if zoom already started, don't tilt
- totalDelta += detector.getShovePixelsDelta();
- 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));
- }
+ void notifyOnScaleListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScale(detector);
+ }
+ }
- if (!tiltGestureOccurred) {
- return false;
- }
+ void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) {
+ for (MapboxMap.OnScaleListener listener : onScaleListenerList) {
+ listener.onScaleEnd(detector);
+ }
+ }
- // Get tilt value (scale and clamp)
- double pitch = transform.getTilt();
- pitch -= 0.1 * detector.getShovePixelsDelta();
- pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch));
+ void notifyOnShoveBeginListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveBegin(detector);
+ }
+ }
- // Tilt the map
- transform.setTilt(pitch);
- return true;
+ void notifyOnShoveListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShove(detector);
+ }
+ }
+
+ void notifyOnShoveEndListeners(ShoveGestureDetector detector) {
+ for (MapboxMap.OnShoveListener listener : onShoveListenerList) {
+ listener.onShoveEnd(detector);
}
}
@@ -963,4 +1069,46 @@ final class MapGestureDetector {
void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) {
onScrollListenerList.remove(onScrollListener);
}
-}
+
+ void addOnMoveListener(MapboxMap.OnMoveListener listener) {
+ onMoveListenerList.add(listener);
+ }
+
+ void removeOnMoveListener(MapboxMap.OnMoveListener listener) {
+ onMoveListenerList.remove(listener);
+ }
+
+ void addOnRotateListener(MapboxMap.OnRotateListener listener) {
+ onRotateListenerList.add(listener);
+ }
+
+ void removeOnRotateListener(MapboxMap.OnRotateListener listener) {
+ onRotateListenerList.remove(listener);
+ }
+
+ void addOnScaleListener(MapboxMap.OnScaleListener listener) {
+ onScaleListenerList.add(listener);
+ }
+
+ void removeOnScaleListener(MapboxMap.OnScaleListener listener) {
+ onScaleListenerList.remove(listener);
+ }
+
+ void addShoveListener(MapboxMap.OnShoveListener listener) {
+ onShoveListenerList.add(listener);
+ }
+
+ void removeShoveListener(MapboxMap.OnShoveListener listener) {
+ onShoveListenerList.remove(listener);
+ }
+
+ AndroidGesturesManager getGesturesManager() {
+ return gesturesManager;
+ }
+
+ void setGesturesManager(Context context, AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
+ boolean setDefaultMutuallyExclusives) {
+ initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives);
+ initializeGestureListeners(context, attachDefaultListeners);
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
index d1f01a30f7..08110ff326 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java
@@ -13,24 +13,22 @@ import android.view.ViewConfiguration;
* <p>
* <ul>
* <li> Uses {@link Transform} to change the map state</li>
- * <li> Uses {@link TrackingSettings} to verify validity of the current tracking mode.</li>
* <li> Uses {@link UiSettings} to verify validity of user restricted movement.</li>
* </ul>
* <p>
*/
final class MapKeyListener {
- private final TrackingSettings trackingSettings;
private final Transform transform;
private final UiSettings uiSettings;
+ private final MapGestureDetector mapGestureDetector;
private TrackballLongPressTimeOut currentTrackballLongPressTimeOut;
- MapKeyListener(@NonNull Transform transform, @NonNull TrackingSettings trackingSettings,
- @NonNull UiSettings uiSettings) {
+ MapKeyListener(Transform transform, UiSettings uiSettings, MapGestureDetector mapGestureDetector) {
this.transform = transform;
- this.trackingSettings = trackingSettings;
this.uiSettings = uiSettings;
+ this.mapGestureDetector = mapGestureDetector;
}
/**
@@ -55,7 +53,7 @@ final class MapKeyListener {
return true;
case KeyEvent.KEYCODE_DPAD_LEFT:
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
@@ -67,7 +65,7 @@ final class MapKeyListener {
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
@@ -79,7 +77,7 @@ final class MapKeyListener {
return true;
case KeyEvent.KEYCODE_DPAD_UP:
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
@@ -91,7 +89,7 @@ final class MapKeyListener {
return true;
case KeyEvent.KEYCODE_DPAD_DOWN:
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
@@ -128,7 +126,7 @@ final class MapKeyListener {
// Zoom out
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(false, focalPoint);
+ mapGestureDetector.zoomOutAnimated(focalPoint, true);
return true;
default:
@@ -164,7 +162,7 @@ final class MapKeyListener {
// Zoom in
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(true, focalPoint);
+ mapGestureDetector.zoomInAnimated(focalPoint, true);
return true;
}
@@ -183,7 +181,7 @@ final class MapKeyListener {
switch (event.getActionMasked()) {
// The trackball was rotated
case MotionEvent.ACTION_MOVE:
- if (!trackingSettings.isScrollGestureCurrentlyEnabled()) {
+ if (!uiSettings.isScrollGesturesEnabled()) {
return false;
}
@@ -219,7 +217,7 @@ final class MapKeyListener {
if (currentTrackballLongPressTimeOut != null) {
// Zoom in
PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(true, focalPoint);
+ mapGestureDetector.zoomInAnimated(focalPoint, true);
}
return true;
@@ -261,7 +259,7 @@ final class MapKeyListener {
if (!cancelled) {
// Zoom out
PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2);
- transform.zoom(false, pointF);
+ mapGestureDetector.zoomOutAnimated(pointF, true);
// Ensure the up action is not run
currentTrackballLongPressTimeOut = null;
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 3c5b82c3b0..22d5dd8f19 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -23,6 +23,12 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ZoomButtonsController;
+import com.mapbox.android.gestures.AndroidGesturesManager;
+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;
@@ -32,11 +38,8 @@ import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer;
import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer;
import com.mapbox.mapboxsdk.maps.widgets.CompassView;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.net.ConnectivityReceiver;
import com.mapbox.mapboxsdk.storage.FileSource;
-import com.mapbox.services.android.telemetry.MapboxTelemetry;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -78,7 +81,6 @@ public class MapView extends FrameLayout {
private boolean destroyed;
private boolean hasSurface;
- private MyLocationView myLocationView;
private CompassView compassView;
private PointF focalPoint;
private ImageView attrView;
@@ -126,7 +128,6 @@ public class MapView extends FrameLayout {
// inflate view
View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this);
compassView = (CompassView) view.findViewById(R.id.compassView);
- myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView);
attrView = (ImageView) view.findViewById(R.id.attributionView);
logoView = (ImageView) view.findViewById(R.id.logoView);
@@ -146,10 +147,7 @@ public class MapView extends FrameLayout {
focalPointInvalidator.addListener(createFocalPointChangeListener());
// callback for registering touch listeners
- RegisterTouchListener registerTouchListener = new RegisterTouchListener();
-
- // callback for zooming in the camera
- CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator();
+ GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener();
// callback for camera change events
final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher();
@@ -157,10 +155,6 @@ public class MapView extends FrameLayout {
// setup components for MapboxMap creation
Projection proj = new Projection(nativeMapView);
UiSettings uiSettings = new UiSettings(proj, focalPointInvalidator, compassView, attrView, logoView);
- TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator,
- zoomInvalidator);
- MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj,
- focalPointInvalidator);
LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>();
MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer));
IconManager iconManager = new IconManager(nativeMapView);
@@ -171,31 +165,29 @@ public class MapView extends FrameLayout {
ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray);
AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray,
markerViewManager, iconManager, annotations, markers, polygons, polylines, shapeAnnotations);
- Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings,
+ Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(),
cameraChangeDispatcher);
- mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj,
- registerTouchListener, annotationManager, cameraChangeDispatcher);
- focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener());
+ mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, proj, registerTouchListener,
+ annotationManager, cameraChangeDispatcher);
mapCallback.attachMapboxMap(mapboxMap);
// user input
- mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings,
+ mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings,
annotationManager, cameraChangeDispatcher);
- mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings);
+ mapKeyListener = new MapKeyListener(transform, uiSettings, mapGestureDetector);
// overlain zoom buttons
mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this));
- MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform,
- cameraChangeDispatcher, getWidth(), getHeight());
+ MapZoomControllerListener zoomListener = new MapZoomControllerListener(
+ mapGestureDetector, cameraChangeDispatcher, getWidth(), getHeight());
mapZoomButtonController.bind(uiSettings, zoomListener);
compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher));
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);
@@ -265,7 +257,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.
@@ -276,7 +268,12 @@ public class MapView extends FrameLayout {
@UiThread
public void onCreate(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
- MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent());
+ MapboxTelemetry telemetry = Telemetry.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,10 +282,12 @@ public class MapView extends FrameLayout {
private void initialiseDrawingSurface(MapboxMapOptions options) {
if (options.getTextureMode()) {
TextureView textureView = new TextureView(getContext());
- mapRenderer = new TextureViewMapRenderer(getContext(), textureView, options.getLocalIdeographFontFamily()) {
+ String localFontFamily = options.getLocalIdeographFontFamily();
+ boolean translucentSurface = options.getTranslucentTextureSurface();
+ mapRenderer = new TextureViewMapRenderer(getContext(), textureView, localFontFamily, translucentSurface) {
@Override
protected void onSurfaceCreated(GL10 gl, EGLConfig config) {
- initRenderSurface();
+ MapView.this.onSurfaceCreated();
super.onSurfaceCreated(gl, config);
}
};
@@ -300,7 +299,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);
}
};
@@ -312,14 +311,14 @@ 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();
+ MapView.this.initialiseMap();
mapboxMap.onStart();
}
}
@@ -383,6 +382,7 @@ public class MapView extends FrameLayout {
public void onStop() {
if (mapboxMap != null) {
// map was destroyed before it was started
+ mapGestureDetector.cancelAnimators();
mapboxMap.onStop();
}
@@ -916,7 +916,7 @@ public class MapView extends FrameLayout {
}
}
- private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener {
+ private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener {
@Override
public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) {
@@ -977,22 +977,75 @@ public class MapView extends FrameLayout {
public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) {
mapGestureDetector.removeOnFlingListener(listener);
}
+
+ @Override
+ public void onAddMoveListener(MapboxMap.OnMoveListener listener) {
+ mapGestureDetector.addOnMoveListener(listener);
+ }
+
+ @Override
+ public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) {
+ mapGestureDetector.removeOnMoveListener(listener);
+ }
+
+ @Override
+ public void onAddRotateListener(MapboxMap.OnRotateListener listener) {
+ mapGestureDetector.addOnRotateListener(listener);
+ }
+
+ @Override
+ public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) {
+ mapGestureDetector.removeOnRotateListener(listener);
+ }
+
+ @Override
+ public void onAddScaleListener(MapboxMap.OnScaleListener listener) {
+ mapGestureDetector.addOnScaleListener(listener);
+ }
+
+ @Override
+ public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) {
+ mapGestureDetector.removeOnScaleListener(listener);
+ }
+
+ @Override
+ public void onAddShoveListener(MapboxMap.OnShoveListener listener) {
+ mapGestureDetector.addShoveListener(listener);
+ }
+
+ @Override
+ public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) {
+ mapGestureDetector.removeShoveListener(listener);
+ }
+
+ @Override
+ public AndroidGesturesManager getGesturesManager() {
+ return mapGestureDetector.getGesturesManager();
+ }
+
+ @Override
+ public void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
+ boolean setDefaultMutuallyExclusives) {
+ mapGestureDetector.setGesturesManager(
+ getContext(), gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives);
+ }
+
+ @Override
+ public void cancelAllVelocityAnimations() {
+ mapGestureDetector.cancelAnimators();
+ }
}
private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener {
private final MapGestureDetector mapGestureDetector;
- private final UiSettings uiSettings;
- private final Transform transform;
private final CameraChangeDispatcher cameraChangeDispatcher;
private final float mapWidth;
private final float mapHeight;
- MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform,
- CameraChangeDispatcher dispatcher, float mapWidth, float mapHeight) {
+ MapZoomControllerListener(MapGestureDetector detector, CameraChangeDispatcher dispatcher,
+ float mapWidth, float mapHeight) {
this.mapGestureDetector = detector;
- this.uiSettings = uiSettings;
- this.transform = transform;
this.cameraChangeDispatcher = dispatcher;
this.mapWidth = mapWidth;
this.mapHeight = mapHeight;
@@ -1012,32 +1065,13 @@ public class MapView extends FrameLayout {
}
private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) {
- if (focalPoint != null) {
- transform.zoom(zoomIn, focalPoint);
- } else {
- PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2);
- transform.zoom(zoomIn, centerPoint);
- }
- }
- }
-
- private class CameraZoomInvalidator implements TrackingSettings.CameraZoomInvalidator {
-
- @Override
- public void zoomTo(double zoomLevel) {
- Transform transform = mapboxMap.getTransform();
- double currentZoomLevel = transform.getCameraPosition().zoom;
- if (currentZoomLevel < zoomLevel) {
- setZoom(zoomLevel, mapGestureDetector.getFocalPoint(), transform);
+ if (focalPoint == null) {
+ focalPoint = new PointF(mapWidth / 2, mapHeight / 2);
}
- }
-
- private void setZoom(double zoomLevel, @Nullable PointF focalPoint, @NonNull Transform transform) {
- if (focalPoint != null) {
- transform.setZoom(zoomLevel, focalPoint);
+ if (zoomIn) {
+ mapGestureDetector.zoomInAnimated(focalPoint, true);
} else {
- PointF centerPoint = new PointF(getMeasuredWidth() / 2, getMeasuredHeight() / 2);
- transform.setZoom(zoomLevel, centerPoint);
+ mapGestureDetector.zoomOutAnimated(focalPoint, true);
}
}
}
@@ -1090,4 +1124,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 a6da4c57fb..cfa7143671 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
@@ -4,7 +4,6 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.graphics.RectF;
-import android.location.Location;
import android.os.Bundle;
import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
@@ -16,6 +15,13 @@ import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
+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;
@@ -31,19 +37,13 @@ import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.geometry.LatLngBounds;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
-import com.mapbox.mapboxsdk.style.layers.Filter;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.Source;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.Geometry;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
@@ -66,37 +66,30 @@ public final class MapboxMap {
private final NativeMapView nativeMapView;
private final UiSettings uiSettings;
- private final TrackingSettings trackingSettings;
private final Projection projection;
private final Transform transform;
private final AnnotationManager annotationManager;
- private final MyLocationViewSettings myLocationViewSettings;
private final CameraChangeDispatcher cameraChangeDispatcher;
- private final OnRegisterTouchListener onRegisterTouchListener;
+ private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener;
private MapboxMap.OnFpsChangedListener onFpsChangedListener;
- private PointF focalPoint;
- MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking,
- MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener,
- AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) {
+ MapboxMap(NativeMapView map, Transform transform, UiSettings ui, Projection projection,
+ OnGesturesManagerInteractionListener listener, AnnotationManager annotations,
+ CameraChangeDispatcher cameraChangeDispatcher) {
this.nativeMapView = map;
this.uiSettings = ui;
- this.trackingSettings = tracking;
this.projection = projection;
- this.myLocationViewSettings = myLocationView;
this.annotationManager = annotations.bind(this);
this.transform = transform;
- this.onRegisterTouchListener = listener;
+ this.onGesturesManagerInteractionListener = listener;
this.cameraChangeDispatcher = cameraChangeDispatcher;
}
void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) {
transform.initialise(this, options);
uiSettings.initialise(context, options);
- myLocationViewSettings.initialise(options);
- trackingSettings.initialise(options);
// Map configuration
setDebugActive(options.getDebugActive());
@@ -110,7 +103,6 @@ public final class MapboxMap {
*/
void onStart() {
nativeMapView.update();
- trackingSettings.onStart();
if (TextUtils.isEmpty(nativeMapView.getStyleUrl()) && TextUtils.isEmpty(nativeMapView.getStyleJson())) {
// if user hasn't loaded a Style yet
nativeMapView.setStyleUrl(Style.MAPBOX_STREETS);
@@ -121,7 +113,6 @@ public final class MapboxMap {
* Called when the hosting Activity/Fragment onStop() method is called.
*/
void onStop() {
- trackingSettings.onStop();
}
/**
@@ -133,9 +124,7 @@ public final class MapboxMap {
outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition());
outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug());
outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl());
- trackingSettings.onSaveInstanceState(outState);
uiSettings.onSaveInstanceState(outState);
- myLocationViewSettings.onSaveInstanceState(outState);
}
/**
@@ -146,9 +135,7 @@ public final class MapboxMap {
void onRestoreInstanceState(Bundle savedInstanceState) {
final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION);
- myLocationViewSettings.onRestoreInstanceState(savedInstanceState);
uiSettings.onRestoreInstanceState(savedInstanceState);
- trackingSettings.onRestoreInstanceState(savedInstanceState);
if (cameraPosition != null) {
moveCamera(CameraUpdateFactory.newCameraPosition(
@@ -188,7 +175,6 @@ public final class MapboxMap {
* Called when the region is changing or has changed.
*/
void onUpdateRegionChange() {
- trackingSettings.update();
annotationManager.update();
}
@@ -556,38 +542,6 @@ public final class MapboxMap {
}
//
- // TrackingSettings
- //
-
- /**
- * Gets the tracking interface settings for the map.
- *
- * @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.
- */
- @Deprecated
- public TrackingSettings getTrackingSettings() {
- return trackingSettings;
- }
-
- //
- // MyLocationViewSettings
- //
-
- /**
- * Gets the settings of the user location for the map.
- *
- * @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.
- */
- @Deprecated
- public MyLocationViewSettings getMyLocationViewSettings() {
- return myLocationViewSettings;
- }
-
- //
// Projection
//
@@ -602,7 +556,7 @@ public final class MapboxMap {
}
//
- //
+ // Light
//
/**
@@ -620,47 +574,6 @@ public final class MapboxMap {
//
/**
- * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the
- * camera on the LatLng object.
- *
- * @param latLng Target location to change to
- */
- public void setLatLng(@NonNull LatLng latLng) {
- nativeMapView.setLatLng(latLng);
- }
-
- /**
- * Moves the camera viewpoint to a particular zoom level.
- *
- * @param zoom Zoom level to change to
- */
- public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) {
- if (focalPoint == null) {
- focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2);
- }
- nativeMapView.setZoom(zoom, focalPoint, 0);
- }
-
- /**
- * Moves the camera viewpoint angle to a particular angle in degrees.
- *
- * @param tilt Tilt angle to change to
- */
- public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) {
- nativeMapView.setPitch(tilt, 0);
- }
-
- /**
- * Moves the camera viewpoint direction to a particular angle in degrees.
- *
- * @param bearing Direction angle to change to
- */
- public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION)
- double bearing) {
- nativeMapView.setBearing(bearing);
- }
-
- /**
* Cancels ongoing animations.
* <p>
* This invokes the {@link CancelableCallback} for ongoing camera updates.
@@ -787,11 +700,6 @@ public final class MapboxMap {
* unless specified within {@link CameraUpdate}. A callback can be used to be notified when
* easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it
* will return the current location of the camera in flight.
- * <p>
- * Note that this will cancel location tracking mode if enabled. You can change this behaviour by calling
- * {@link com.mapbox.mapboxsdk.maps.TrackingSettings#setDismissLocationTrackingOnGesture(boolean)} with false before
- * invoking this method and calling it with true in the {@link CancelableCallback#onFinish()}.
- * </p>
*
* @param update The change that should be applied to the camera.
* @param durationMs The duration of the animation in milliseconds. This must be strictly
@@ -813,11 +721,6 @@ public final class MapboxMap {
* unless specified within {@link CameraUpdate}. A callback can be used to be notified when
* easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it
* will return the current location of the camera in flight.
- * <p>
- * Note that this will cancel location tracking mode if enabled. You can change this behaviour by calling
- * {@link com.mapbox.mapboxsdk.maps.TrackingSettings#setDismissLocationTrackingOnGesture(boolean)} with false before
- * invoking this method and calling it with true in the {@link CancelableCallback#onFinish()}.
- * </p>
*
* @param update The change that should be applied to the camera.
* @param durationMs The duration of the animation in milliseconds. This must be strictly
@@ -1647,20 +1550,9 @@ public final class MapboxMap {
* @param padding the padding to apply to the bounds
* @return the camera position that fits the bounds and padding
*/
- public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) {
- // calculate and set additional bounds padding
- int[] mapPadding = getPadding();
- for (int i = 0; i < padding.length; i++) {
- padding[i] = mapPadding[i] + padding[i];
- }
- projection.setContentPadding(padding, myLocationViewSettings.getPadding());
-
+ public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) {
// get padded camera position from LatLngBounds
- CameraPosition cameraPosition = nativeMapView.getCameraForLatLngBounds(latLngBounds);
-
- // reset map padding
- setPadding(mapPadding);
- return cameraPosition;
+ return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding);
}
/**
@@ -1672,19 +1564,8 @@ public final class MapboxMap {
* @return the camera position that fits the bounds and padding
*/
public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) {
- // calculate and set additional bounds padding
- int[] mapPadding = getPadding();
- for (int i = 0; i < padding.length; i++) {
- padding[i] = mapPadding[i] + padding[i];
- }
- projection.setContentPadding(padding, myLocationViewSettings.getPadding());
-
- // get padded camera position from LatLngBounds
- CameraPosition cameraPosition = nativeMapView.getCameraForGeometry(geometry, bearing);
-
- // reset map padding
- setPadding(mapPadding);
- return cameraPosition;
+ // get padded camera position from Geometry
+ return nativeMapView.getCameraForGeometry(geometry, bearing, padding);
}
//
@@ -1693,13 +1574,13 @@ public final class MapboxMap {
/**
* <p>
- * Sets the distance from the edges of the map view’s frame to the edges of the map
- * view’s logical viewport.
+ * Sets the distance from the edges of the map view&#x27;s frame to the edges of the map
+ * view&#x27s logical viewport.
* </p>
* <p>
* When the value of this property is equal to {0,0,0,0}, viewport
- * properties such as `centerCoordinate` assume a viewport that matches the map
- * view’s frame. Otherwise, those properties are inset, excluding part of the
+ * properties such as &#x27;centerCoordinate&#x27; assume a viewport that matches the map
+ * view&#x27;s frame. Otherwise, those properties are inset, excluding part of the
* frame from the viewport. For instance, if the only the top edge is inset, the
* map center is effectively shifted downward.
* </p>
@@ -1714,7 +1595,7 @@ public final class MapboxMap {
}
private void setPadding(int[] padding) {
- projection.setContentPadding(padding, myLocationViewSettings.getPadding());
+ projection.setContentPadding(padding);
uiSettings.invalidate();
}
@@ -1759,7 +1640,7 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- public void addOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ public void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
cameraChangeDispatcher.addOnCameraIdleListener(listener);
}
@@ -1768,7 +1649,7 @@ public final class MapboxMap {
*
* @param listener the listener to remove
*/
- public void removeOnCameraIdleListener(@Nullable OnCameraIdleListener listener) {
+ public void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) {
cameraChangeDispatcher.removeOnCameraIdleListener(listener);
}
@@ -1789,7 +1670,7 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- public void addOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ public void addOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.addOnCameraMoveCancelListener(listener);
}
@@ -1798,7 +1679,7 @@ public final class MapboxMap {
*
* @param listener the listener to remove
*/
- public void removeOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) {
+ public void removeOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) {
cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener);
}
@@ -1819,7 +1700,7 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- public void addOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ public void addOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.addOnCameraMoveStartedListener(listener);
}
@@ -1828,7 +1709,7 @@ public final class MapboxMap {
*
* @param listener the listener to remove
*/
- public void removeOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) {
+ public void removeOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) {
cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener);
}
@@ -1849,7 +1730,7 @@ public final class MapboxMap {
*
* @param listener the listener to notify
*/
- public void addOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ public void addOnCameraMoveListener(@NonNull OnCameraMoveListener listener) {
cameraChangeDispatcher.addOnCameraMoveListener(listener);
}
@@ -1858,7 +1739,7 @@ public final class MapboxMap {
*
* @param listener the listener to remove
*/
- public void removeOnCameraMoveListener(@Nullable OnCameraMoveListener listener) {
+ public void removeOnCameraMoveListener(@NonNull OnCameraMoveListener listener) {
cameraChangeDispatcher.removeOnCameraMoveListener(listener);
}
@@ -1887,27 +1768,25 @@ public final class MapboxMap {
*/
@Deprecated
public void setOnScrollListener(@Nullable OnScrollListener listener) {
- onRegisterTouchListener.onSetScrollListener(listener);
+ onGesturesManagerInteractionListener.onSetScrollListener(listener);
}
/**
* Adds a callback that's invoked when the map is scrolled.
*
* @param listener The callback that's invoked when the map is scrolled.
- * To unset the callback, use null.
*/
- public void addOnScrollListener(@Nullable OnScrollListener listener) {
- onRegisterTouchListener.onAddScrollListener(listener);
+ public void addOnScrollListener(@NonNull OnScrollListener listener) {
+ onGesturesManagerInteractionListener.onAddScrollListener(listener);
}
/**
* Removes a callback that's invoked when the map is scrolled.
*
* @param listener The callback that's invoked when the map is scrolled.
- * To unset the callback, use null.
*/
- public void removeOnScrollListener(@Nullable OnScrollListener listener) {
- onRegisterTouchListener.onRemoveScrollListener(listener);
+ public void removeOnScrollListener(@NonNull OnScrollListener listener) {
+ onGesturesManagerInteractionListener.onRemoveScrollListener(listener);
}
/**
@@ -1919,27 +1798,131 @@ public final class MapboxMap {
*/
@Deprecated
public void setOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onSetFlingListener(listener);
+ onGesturesManagerInteractionListener.onSetFlingListener(listener);
}
/**
* Adds a callback that's invoked when the map is flinged.
*
* @param listener The callback that's invoked when the map is flinged.
- * To unset the callback, use null.
*/
- public void addOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onAddFlingListener(listener);
+ public void addOnFlingListener(@NonNull OnFlingListener listener) {
+ onGesturesManagerInteractionListener.onAddFlingListener(listener);
}
/**
* Removes a callback that's invoked when the map is flinged.
*
* @param listener The callback that's invoked when the map is flinged.
- * To unset the callback, use null.
*/
- public void removeOnFlingListener(@Nullable OnFlingListener listener) {
- onRegisterTouchListener.onRemoveFlingListener(listener);
+ public void removeOnFlingListener(@NonNull OnFlingListener listener) {
+ onGesturesManagerInteractionListener.onRemoveFlingListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is moved.
+ *
+ * @param listener The callback that's invoked when the map is moved.
+ */
+ public void addOnMoveListener(@NonNull OnMoveListener listener) {
+ onGesturesManagerInteractionListener.onAddMoveListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is moved.
+ *
+ * @param listener The callback that's invoked when the map is moved.
+ */
+ public void removeOnMoveListener(@NonNull OnMoveListener listener) {
+ onGesturesManagerInteractionListener.onRemoveMoveListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is rotated.
+ *
+ * @param listener The callback that's invoked when the map is rotated.
+ */
+ public void addOnRotateListener(@NonNull OnRotateListener listener) {
+ onGesturesManagerInteractionListener.onAddRotateListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is rotated.
+ *
+ * @param listener The callback that's invoked when the map is rotated.
+ */
+ public void removeOnRotateListener(@NonNull OnRotateListener listener) {
+ onGesturesManagerInteractionListener.onRemoveRotateListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is scaled.
+ *
+ * @param listener The callback that's invoked when the map is scaled.
+ */
+ public void addOnScaleListener(@NonNull OnScaleListener listener) {
+ onGesturesManagerInteractionListener.onAddScaleListener(listener);
+ }
+
+ /**
+ * Removes a callback that's invoked when the map is scaled.
+ *
+ * @param listener The callback that's invoked when the map is scaled.
+ */
+ public void removeOnScaleListener(@NonNull OnScaleListener listener) {
+ onGesturesManagerInteractionListener.onRemoveScaleListener(listener);
+ }
+
+ /**
+ * Adds a callback that's invoked when the map is tilted.
+ *
+ * @param listener The callback that's invoked when the map is tilted.
+ */
+ public void addOnShoveListener(@NonNull OnShoveListener listener) {
+ onGesturesManagerInteractionListener.onAddShoveListener(listener);
+ }
+
+ /**
+ * Remove a callback that's invoked when the map is tilted.
+ *
+ * @param listener The callback that's invoked when the map is tilted.
+ */
+ public void removeOnShoveListener(@NonNull OnShoveListener listener) {
+ onGesturesManagerInteractionListener.onRemoveShoveListener(listener);
+ }
+
+ /**
+ * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s
+ * registered by the {@link MapView}.
+ *
+ * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events.
+ * @param attachDefaultListeners If true, pre-defined listeners will be attach
+ * to change map based on {@link AndroidGesturesManager} callbacks.
+ * @param setDefaultMutuallyExclusives If true, pre-defined mutually exclusive gesture sets
+ * will be added to the passed gestures manager.
+ * @see <a href="https://github.com/mapbox/mapbox-gestures-android">mapbox-gestures-android library</a>
+ */
+ public void setGesturesManager(AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners,
+ boolean setDefaultMutuallyExclusives) {
+ onGesturesManagerInteractionListener.setGesturesManager(
+ androidGesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives);
+ }
+
+ /**
+ * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s
+ * registered by the {@link MapView}
+ *
+ * @return Current gestures manager.
+ */
+ public AndroidGesturesManager getGesturesManager() {
+ return onGesturesManagerInteractionListener.getGesturesManager();
+ }
+
+ /**
+ * Interrupts any ongoing gesture velocity animations.
+ */
+ public void cancelAllVelocityAnimations() {
+ onGesturesManagerInteractionListener.cancelAllVelocityAnimations();
}
/**
@@ -1951,27 +1934,25 @@ public final class MapboxMap {
*/
@Deprecated
public void setOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onSetMapClickListener(listener);
+ onGesturesManagerInteractionListener.onSetMapClickListener(listener);
}
/**
* Adds a callback that's invoked when the user clicks on the map view.
*
* @param listener The callback that's invoked when the user clicks on the map view.
- * To unset the callback, use null.
*/
- public void addOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onAddMapClickListener(listener);
+ public void addOnMapClickListener(@NonNull OnMapClickListener listener) {
+ onGesturesManagerInteractionListener.onAddMapClickListener(listener);
}
/**
* Removes a callback that's invoked when the user clicks on the map view.
*
* @param listener The callback that's invoked when the user clicks on the map view.
- * To unset the callback, use null.
*/
- public void removeOnMapClickListener(@Nullable OnMapClickListener listener) {
- onRegisterTouchListener.onRemoveMapClickListener(listener);
+ public void removeOnMapClickListener(@NonNull OnMapClickListener listener) {
+ onGesturesManagerInteractionListener.onRemoveMapClickListener(listener);
}
/**
@@ -1983,27 +1964,25 @@ public final class MapboxMap {
*/
@Deprecated
public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onSetMapLongClickListener(listener);
+ onGesturesManagerInteractionListener.onSetMapLongClickListener(listener);
}
/**
* Adds a callback that's invoked when the user long clicks on the map view.
*
* @param listener The callback that's invoked when the user long clicks on the map view.
- * To unset the callback, use null.
*/
- public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onAddMapLongClickListener(listener);
+ public void addOnMapLongClickListener(@NonNull OnMapLongClickListener listener) {
+ onGesturesManagerInteractionListener.onAddMapLongClickListener(listener);
}
/**
* Removes a callback that's invoked when the user long clicks on the map view.
*
* @param listener The callback that's invoked when the user long clicks on the map view.
- * To unset the callback, use null.
*/
- public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) {
- onRegisterTouchListener.onRemoveMapLongClickListener(listener);
+ public void removeOnMapLongClickListener(@NonNull OnMapLongClickListener listener) {
+ onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener);
}
/**
@@ -2064,107 +2043,6 @@ public final class MapboxMap {
}
//
- // User location
- //
-
- /**
- * Returns the status of the my-location layer.
- *
- * @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.
- */
- @Deprecated
- public boolean isMyLocationEnabled() {
- return trackingSettings.isMyLocationEnabled();
- }
-
- /**
- * <p>
- * Enables or disables the my-location layer.
- * While enabled, the my-location layer continuously draws an indication of a user's current
- * location and bearing.
- * </p>
- * In order to use the my-location layer feature you need to request permission for either
- * android.Manifest.permission#ACCESS_COARSE_LOCATION or android.Manifest.permission#ACCESS_FINE_LOCATION.
- *
- * @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.
- */
- @Deprecated
- public void setMyLocationEnabled(boolean enabled) {
- trackingSettings.setMyLocationEnabled(enabled);
- }
-
- /**
- * Returns the currently displayed user location, or null if there is no location data available.
- *
- * @return The currently displayed user location.
- * @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
- */
- @Nullable
- @Deprecated
- public Location getMyLocation() {
- return trackingSettings.getMyLocation();
- }
-
- /**
- * Sets a callback that's invoked when the the My Location view
- * (which signifies the user's location) changes location.
- *
- * @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.
- */
- @Deprecated
- public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener
- listener) {
- trackingSettings.setOnMyLocationChangeListener(listener);
- }
-
- /**
- * Replaces the location source of the my-location layer.
- *
- * @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.
- */
- @Deprecated
- public void setLocationSource(@Nullable LocationEngine locationSource) {
- trackingSettings.setLocationSource(locationSource);
- }
-
- /**
- * Sets a callback that's invoked when the location tracking mode changes.
- *
- * @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.
- */
- @Deprecated
- public void setOnMyLocationTrackingModeChangeListener(
- @Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
- trackingSettings.setOnMyLocationTrackingModeChangeListener(listener);
- }
-
- /**
- * Sets a callback that's invoked when the bearing tracking mode changes.
- *
- * @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.
- */
- @Deprecated
- public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) {
- trackingSettings.setOnMyBearingTrackingModeChangeListener(listener);
- }
-
- //
// Invalidate
//
@@ -2194,13 +2072,13 @@ public final class MapboxMap {
* Queries the map for rendered features
*
* @param coordinates the point to query
- * @param filter filters the returned features
+ * @param filter filters the returned features with an expression
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
- @Nullable Filter.Statement filter,
+ @Nullable Expression filter,
@Nullable String... layerIds) {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
@@ -2222,26 +2100,17 @@ public final class MapboxMap {
* Queries the map for rendered features
*
* @param coordinates the box to query
- * @param filter filters the returned features
+ * @param filter filters the returned features with an expression
* @param layerIds optionally - only query these layers
* @return the list of feature
*/
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
- @Nullable Filter.Statement filter,
+ @Nullable Expression filter,
@Nullable String... layerIds) {
return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter);
}
- FocalPointChangeListener createFocalPointChangeListener() {
- return new FocalPointChangeListener() {
- @Override
- public void onFocalPointChanged(PointF pointF) {
- focalPoint = pointF;
- }
- };
- }
-
//
// Interfaces
//
@@ -2262,7 +2131,9 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when the map is scrolled.
*
* @see MapboxMap#setOnScrollListener(OnScrollListener)
+ * @deprecated Use {@link OnMoveListener} instead.
*/
+ @Deprecated
public interface OnScrollListener {
/**
* Called when the map is scrolled.
@@ -2271,6 +2142,58 @@ public final class MapboxMap {
}
/**
+ * Interface definition for a callback to be invoked when the map is moved.
+ *
+ * @see MapboxMap#addOnMoveListener(OnMoveListener)
+ */
+ public interface OnMoveListener {
+ void onMoveBegin(MoveGestureDetector detector);
+
+ void onMove(MoveGestureDetector detector);
+
+ void onMoveEnd(MoveGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is rotated.
+ *
+ * @see MapboxMap#addOnRotateListener(OnRotateListener)
+ */
+ public interface OnRotateListener {
+ void onRotateBegin(RotateGestureDetector detector);
+
+ void onRotate(RotateGestureDetector detector);
+
+ void onRotateEnd(RotateGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is scaled.
+ *
+ * @see MapboxMap#addOnScaleListener(OnScaleListener)
+ */
+ public interface OnScaleListener {
+ void onScaleBegin(StandardScaleGestureDetector detector);
+
+ void onScale(StandardScaleGestureDetector detector);
+
+ void onScaleEnd(StandardScaleGestureDetector detector);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when the map is tilted.
+ *
+ * @see MapboxMap#addOnShoveListener(OnShoveListener)
+ */
+ public interface OnShoveListener {
+ void onShoveBegin(ShoveGestureDetector detector);
+
+ void onShove(ShoveGestureDetector detector);
+
+ void onShoveEnd(ShoveGestureDetector detector);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the camera changes position.
*
* @deprecated Replaced by {@link MapboxMap.OnCameraMoveStartedListener}, {@link MapboxMap.OnCameraMoveListener} and
@@ -2372,7 +2295,7 @@ public final class MapboxMap {
* Interface definition for a callback to be invoked when a user registers an listener that is
* related to touch and click events.
*/
- interface OnRegisterTouchListener {
+ interface OnGesturesManagerInteractionListener {
void onSetMapClickListener(OnMapClickListener listener);
void onAddMapClickListener(OnMapClickListener listener);
@@ -2396,6 +2319,29 @@ public final class MapboxMap {
void onAddFlingListener(OnFlingListener listener);
void onRemoveFlingListener(OnFlingListener listener);
+
+ void onAddMoveListener(OnMoveListener listener);
+
+ void onRemoveMoveListener(OnMoveListener listener);
+
+ void onAddRotateListener(OnRotateListener listener);
+
+ void onRemoveRotateListener(OnRotateListener listener);
+
+ void onAddScaleListener(OnScaleListener listener);
+
+ void onRemoveScaleListener(OnScaleListener listener);
+
+ void onAddShoveListener(OnShoveListener listener);
+
+ void onRemoveShoveListener(OnShoveListener listener);
+
+ AndroidGesturesManager getGesturesManager();
+
+ void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners,
+ boolean setDefaultMutuallyExclusives);
+
+ void cancelAllVelocityAnimations();
}
/**
@@ -2668,51 +2614,6 @@ public final class MapboxMap {
}
/**
- * Interface definition for a callback to be invoked when the the My Location view changes location.
- *
- * @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener)
- */
- public interface OnMyLocationChangeListener {
- /**
- * Called when the location of the My Location view has changed
- * (be it latitude/longitude, bearing or accuracy).
- *
- * @param location The current location of the My Location view The type of map change event.
- */
- void onMyLocationChange(@Nullable Location location);
- }
-
- /**
- * Interface definition for a callback to be invoked when the the My Location tracking mode changes.
- *
- * @see TrackingSettings#setMyLocationTrackingMode(int)
- */
- public interface OnMyLocationTrackingModeChangeListener {
-
- /**
- * Called when the tracking mode of My Location tracking has changed
- *
- * @param myLocationTrackingMode the current active location tracking mode
- */
- void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode);
- }
-
- /**
- * Interface definition for a callback to be invoked when the the My Location tracking mode changes.
- *
- * @see TrackingSettings#setMyLocationTrackingMode(int)
- */
- public interface OnMyBearingTrackingModeChangeListener {
-
- /**
- * Called when the tracking mode of My Bearing tracking has changed
- *
- * @param myBearingTrackingMode the current active bearing tracking mode
- */
- void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode);
- }
-
- /**
* Interface definition for a callback to be invoked when a task is complete or cancelled.
*/
public interface CancelableCallback {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
index 46dba28b98..bb4e2f9212 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java
@@ -8,10 +8,8 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.ColorInt;
-import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
import android.support.v4.content.res.ResourcesCompat;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -20,7 +18,6 @@ import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.utils.BitmapUtils;
-import com.mapbox.mapboxsdk.utils.ColorUtils;
import java.util.Arrays;
@@ -68,18 +65,6 @@ public class MapboxMapOptions implements Parcelable {
private boolean zoomControlsEnabled = false;
private boolean doubleTapGesturesEnabled = true;
- private boolean myLocationEnabled;
- private Drawable myLocationForegroundDrawable;
- private Drawable myLocationForegroundBearingDrawable;
- private Drawable myLocationBackgroundDrawable;
- @ColorInt
- private int myLocationForegroundTintColor = UNDEFINED_COLOR;
- @ColorInt
- private int myLocationBackgroundTintColor = UNDEFINED_COLOR;
- private int[] myLocationBackgroundPadding;
- private int myLocationAccuracyTintColor;
- private int myLocationAccuracyAlpha;
- private float myLocationAccuracyThreshold;
private boolean prefetchesTiles = true;
private boolean zMediaOverlay = false;
private String localIdeographFontFamily;
@@ -87,6 +72,7 @@ public class MapboxMapOptions implements Parcelable {
private String apiBaseUrl;
private boolean textureMode;
+ private boolean translucentTextureSurface;
private String style;
@@ -129,33 +115,10 @@ public class MapboxMapOptions implements Parcelable {
zoomGesturesEnabled = in.readByte() != 0;
doubleTapGesturesEnabled = in.readByte() != 0;
- myLocationEnabled = in.readByte() != 0;
-
- Bitmap foregroundBitmap = in.readParcelable(getClass().getClassLoader());
- if (foregroundBitmap != null) {
- myLocationForegroundDrawable = new BitmapDrawable(foregroundBitmap);
- }
-
- Bitmap foregroundBearingBitmap = in.readParcelable(getClass().getClassLoader());
- if (foregroundBearingBitmap != null) {
- myLocationForegroundBearingDrawable = new BitmapDrawable(foregroundBearingBitmap);
- }
-
- Bitmap backgroundBitmap = in.readParcelable(getClass().getClassLoader());
- if (backgroundBitmap != null) {
- myLocationBackgroundDrawable = new BitmapDrawable(backgroundBitmap);
- }
-
- myLocationForegroundTintColor = in.readInt();
- myLocationBackgroundTintColor = in.readInt();
- myLocationBackgroundPadding = in.createIntArray();
- myLocationAccuracyAlpha = in.readInt();
- myLocationAccuracyTintColor = in.readInt();
- myLocationAccuracyThreshold = in.readFloat();
-
style = in.readString();
apiBaseUrl = in.readString();
textureMode = in.readByte() != 0;
+ translucentTextureSurface = in.readByte() != 0;
prefetchesTiles = in.readByte() != 0;
zMediaOverlay = in.readByte() != 0;
localIdeographFontFamily = in.readString();
@@ -245,50 +208,10 @@ public class MapboxMapOptions implements Parcelable {
(int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom,
FOUR_DP * pxlRatio))});
- mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false));
- mapboxMapOptions.myLocationForegroundTintColor(
- typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor, UNDEFINED_COLOR));
- mapboxMapOptions.myLocationBackgroundTintColor(
- typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, UNDEFINED_COLOR));
-
- Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationDrawable);
- if (foregroundDrawable == null) {
- foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default);
- }
-
- Drawable foregroundBearingDrawable = typedArray.getDrawable(
- R.styleable.mapbox_MapView_mapbox_myLocationBearingDrawable);
- if (foregroundBearingDrawable == null) {
- foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing);
- }
-
- Drawable backgroundDrawable = typedArray.getDrawable(
- R.styleable.mapbox_MapView_mapbox_myLocationBackgroundDrawable);
- if (backgroundDrawable == null) {
- backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape);
- }
-
- mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
- mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable);
- mapboxMapOptions.myLocationBackgroundPadding(new int[] {
- (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginLeft,
- 0) * pxlRatio),
- (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginTop,
- 0) * pxlRatio),
- (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginRight,
- 0) * pxlRatio),
- (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginBottom,
- 0) * pxlRatio)
- });
- mapboxMapOptions.myLocationAccuracyAlpha(
- typedArray.getInt(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyAlpha, 100));
- mapboxMapOptions.myLocationAccuracyTint(
- typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor,
- ColorUtils.getPrimaryColor(context)));
- mapboxMapOptions.myLocationAccuracyThreshold(
- typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0));
mapboxMapOptions.textureMode(
typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false));
+ mapboxMapOptions.translucentTextureSurface(
+ typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureTranslucentSurface, false));
mapboxMapOptions.setPrefetchesTiles(
typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true));
mapboxMapOptions.renderSurfaceOnTop(
@@ -572,128 +495,6 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Specifies if the user location view is enabled for a map view.
- *
- * @param locationEnabled True and gesture will be enabled
- * @return This
- */
- public MapboxMapOptions locationEnabled(boolean locationEnabled) {
- this.myLocationEnabled = locationEnabled;
- return this;
- }
-
- /**
- * Set the foreground drawables of the MyLocationView.
- *
- * @param myLocationForegroundDrawable the drawable to show as foreground without bearing
- * @param myLocationBearingDrawable the drawable to show as foreground when bearing is disabled
- * @return This
- */
- public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable,
- Drawable myLocationBearingDrawable) {
- this.myLocationForegroundDrawable = myLocationForegroundDrawable;
- this.myLocationForegroundBearingDrawable = myLocationBearingDrawable;
- return this;
- }
-
- /**
- * Set the foreground drawable of the MyLocationView.
- * <p>
- * The same drawable will be used for both bearing as non bearing modes.
- * </p>
- *
- * @param myLocationForegroundDrawable the drawable to show as foreground
- * @return This
- */
- public MapboxMapOptions myLocationForegroundDrawable(Drawable myLocationForegroundDrawable) {
- this.myLocationForegroundDrawable = myLocationForegroundDrawable;
- return this;
- }
-
- /**
- * Set the background drawable of MyLocationView.
- * <p>
- * Padding can be added to provide an offset to the background.
- * </p>
- *
- * @param myLocationBackgroundDrawable the drawable to show as background
- * @return This
- */
- public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) {
- this.myLocationBackgroundDrawable = myLocationBackgroundDrawable;
- return this;
- }
-
- /**
- * Set the foreground tint color of MyLocationView.
- * <p>
- * The color will tint both the foreground and the bearing foreground drawable.
- * </p>
- *
- * @param myLocationForegroundTintColor the color to tint the foreground drawable
- * @return This
- */
- public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) {
- this.myLocationForegroundTintColor = myLocationForegroundTintColor;
- return this;
- }
-
- /**
- * Set the background tint color of MyLocationView.
- *
- * @param myLocationBackgroundTintColor the color to tint the background drawable
- * @return This
- */
- public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) {
- this.myLocationBackgroundTintColor = myLocationBackgroundTintColor;
- return this;
- }
-
- /**
- * Set the MyLocationView padding.
- *
- * @param myLocationBackgroundPadding the color to tint the background
- * @return This
- */
- public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) {
- this.myLocationBackgroundPadding = myLocationBackgroundPadding;
- return this;
- }
-
- /**
- * Set the MyLocationView accuracy circle tint color.
- *
- * @param myLocationAccuracyTintColor the color to tint the accuracy circle
- * @return This
- */
- public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) {
- this.myLocationAccuracyTintColor = myLocationAccuracyTintColor;
- return this;
- }
-
- /**
- * Set the MyLocationView accuracy alpha value.
- *
- * @param alpha the alpha value
- * @return This
- */
- public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
- this.myLocationAccuracyAlpha = alpha;
- return this;
- }
-
- /**
- * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value.
- *
- * @param myLocationAccuracyThreshold Value of accuracy (in meters), below which circle won't be displayed
- * @return This
- */
- public MapboxMapOptions myLocationAccuracyThreshold(float myLocationAccuracyThreshold) {
- this.myLocationAccuracyThreshold = myLocationAccuracyThreshold;
- return this;
- }
-
- /**
* Enable {@link android.view.TextureView} as rendered surface.
* <p>
* Since the 5.2.0 release we replaced our TextureView with an {@link android.opengl.GLSurfaceView}
@@ -711,6 +512,11 @@ public class MapboxMapOptions implements Parcelable {
return this;
}
+ public MapboxMapOptions translucentTextureSurface(boolean translucentTextureSurface) {
+ this.translucentTextureSurface = translucentTextureSurface;
+ return this;
+ }
+
/**
* Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render
* a low resolution preview while more detailed tiles are loaded.
@@ -725,11 +531,11 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Set the font family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’
- * and ‘Hangul Syllables’ ranges.
- *
+ * Set the font family for generating glyphs locally for ideographs in the &#x27;CJK Unified Ideographs&#x27;
+ * and &#x27;Hangul Syllables&#x27; ranges.
+ * <p>
* The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}.
- * Default system fonts are defined in '/system/etc/fonts.xml'
+ * Default system fonts are defined in &#x27;/system/etc/fonts.xml&#x27;
*
* @param fontFamily font family for local ideograph generation.
* @return This
@@ -976,98 +782,6 @@ public class MapboxMapOptions implements Parcelable {
}
/**
- * Get the current configured user location view state for a map view.
- *
- * @return True and user location will be shown
- */
- public boolean getLocationEnabled() {
- return myLocationEnabled;
- }
-
- /**
- * Get the current configured MyLocationView foreground drawable.
- *
- * @return the drawable used as foreground
- */
- public Drawable getMyLocationForegroundDrawable() {
- return myLocationForegroundDrawable;
- }
-
- /**
- * Get the current configured MyLocationView foreground bearing drawable.
- *
- * @return the drawable used as foreground when bearing is enabled
- */
- public Drawable getMyLocationForegroundBearingDrawable() {
- return myLocationForegroundBearingDrawable;
- }
-
- /**
- * Get the current configured MyLocationView background drawable.
- *
- * @return the drawable used as background
- */
- public Drawable getMyLocationBackgroundDrawable() {
- return myLocationBackgroundDrawable;
- }
-
- /**
- * Get the current configured MyLocationView foreground tint color.
- *
- * @return the tint color
- */
- @ColorInt
- public int getMyLocationForegroundTintColor() {
- return myLocationForegroundTintColor;
- }
-
- /**
- * Get the current configured MyLocationView background tint color.
- *
- * @return the tint color
- */
- @ColorInt
- public int getMyLocationBackgroundTintColor() {
- return myLocationBackgroundTintColor;
- }
-
- /**
- * Get the current configured MyLocationView background padding.
- *
- * @return an array describing the padding in a LTRB manner
- */
- public int[] getMyLocationBackgroundPadding() {
- return myLocationBackgroundPadding;
- }
-
- /**
- * Get the current configured MyLocationView accuracy circle color tint value.
- *
- * @return the tint color
- */
- public int getMyLocationAccuracyTintColor() {
- return myLocationAccuracyTintColor;
- }
-
- /**
- * Get the current configured MyLocationView accuracy circle alpha value.
- *
- * @return the alpha value
- */
- public int getMyLocationAccuracyAlpha() {
- return myLocationAccuracyAlpha;
- }
-
- /**
- * Returns current accuracy threshold value (in meters).
- *
- * @return Value of accuracy threshold (in meters), below which circle won't be displayed
- */
- public float getMyLocationAccuracyThreshold() {
- return myLocationAccuracyThreshold;
- }
-
- /**
* Get the current configured debug state for a map view.
*
* @return True indicates debug is enabled.
@@ -1085,9 +799,13 @@ public class MapboxMapOptions implements Parcelable {
return textureMode;
}
+ public boolean getTranslucentTextureSurface() {
+ return translucentTextureSurface;
+ }
+
/**
* Returns the font-family for locally overriding generation of glyphs in the
- * ‘CJK Unified Ideographs’ and ‘Hangul Syllables’ ranges.
+ * &#x27;CJK Unified Ideographs&#x27; and &#x27;Hangul Syllables&#x27; ranges.
*
* @return Local ideograph font family name.
*/
@@ -1141,24 +859,10 @@ public class MapboxMapOptions implements Parcelable {
dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0));
dest.writeByte((byte) (doubleTapGesturesEnabled ? 1 : 0));
- dest.writeByte((byte) (myLocationEnabled ? 1 : 0));
-
- dest.writeParcelable(myLocationForegroundDrawable != null
- ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags);
- dest.writeParcelable(myLocationForegroundBearingDrawable != null
- ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags);
- dest.writeParcelable(myLocationBackgroundDrawable != null
- ? BitmapUtils.getBitmapFromDrawable(myLocationBackgroundDrawable) : null, flags);
- dest.writeInt(myLocationForegroundTintColor);
- dest.writeInt(myLocationBackgroundTintColor);
- dest.writeIntArray(myLocationBackgroundPadding);
- dest.writeInt(myLocationAccuracyAlpha);
- dest.writeInt(myLocationAccuracyTintColor);
- dest.writeFloat(myLocationAccuracyThreshold);
-
dest.writeString(style);
dest.writeString(apiBaseUrl);
dest.writeByte((byte) (textureMode ? 1 : 0));
+ dest.writeByte((byte) (translucentTextureSurface ? 1 : 0));
dest.writeByte((byte) (prefetchesTiles ? 1 : 0));
dest.writeByte((byte) (zMediaOverlay ? 1 : 0));
dest.writeString(localIdeographFontFamily);
@@ -1231,24 +935,6 @@ public class MapboxMapOptions implements Parcelable {
if (doubleTapGesturesEnabled != options.doubleTapGesturesEnabled) {
return false;
}
- if (myLocationEnabled != options.myLocationEnabled) {
- return false;
- }
- if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) {
- return false;
- }
- if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) {
- return false;
- }
- if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) {
- return false;
- }
- if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) {
- return false;
- }
- if (myLocationAccuracyThreshold != options.myLocationAccuracyThreshold) {
- return false;
- }
if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) {
return false;
}
@@ -1261,24 +947,6 @@ public class MapboxMapOptions implements Parcelable {
if (!Arrays.equals(attributionMargins, options.attributionMargins)) {
return false;
}
- if (myLocationForegroundDrawable != null
- ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable)
- : options.myLocationForegroundDrawable != null) {
- return false;
- }
- if (myLocationForegroundBearingDrawable != null
- ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable)
- : options.myLocationForegroundBearingDrawable != null) {
- return false;
- }
- if (myLocationBackgroundDrawable != null
- ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable)
- : options.myLocationBackgroundDrawable != null) {
- return false;
- }
- if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding)) {
- return false;
- }
if (style != null ? !style.equals(options.style) : options.style != null) {
return false;
}
@@ -1326,20 +994,9 @@ public class MapboxMapOptions implements Parcelable {
result = 31 * result + (zoomGesturesEnabled ? 1 : 0);
result = 31 * result + (zoomControlsEnabled ? 1 : 0);
result = 31 * result + (doubleTapGesturesEnabled ? 1 : 0);
- result = 31 * result + (myLocationEnabled ? 1 : 0);
- result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0);
- result = 31 * result + (myLocationForegroundBearingDrawable != null
- ? myLocationForegroundBearingDrawable.hashCode() : 0);
- result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0);
- result = 31 * result + myLocationForegroundTintColor;
- result = 31 * result + myLocationBackgroundTintColor;
- result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding);
- result = 31 * result + myLocationAccuracyTintColor;
- result = 31 * result + myLocationAccuracyAlpha;
- result = 31 * result + (myLocationAccuracyThreshold != +0.0f
- ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0);
result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0);
result = 31 * result + (textureMode ? 1 : 0);
+ result = 31 * result + (translucentTextureSurface ? 1 : 0);
result = 31 * result + (style != null ? style.hashCode() : 0);
result = 31 * result + (prefetchesTiles ? 1 : 0);
result = 31 * result + (zMediaOverlay ? 1 : 0);
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..0e77910c3d 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;
@@ -22,15 +24,13 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.maps.renderer.MapRenderer;
import com.mapbox.mapboxsdk.storage.FileSource;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException;
-import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException;
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;
@@ -229,18 +229,28 @@ final class NativeMapView {
return nativeGetLatLng().wrap();
}
- public CameraPosition getCameraForLatLngBounds(LatLngBounds latLngBounds) {
+ public CameraPosition getCameraForLatLngBounds(LatLngBounds latLngBounds, int[] padding) {
if (isDestroyedOn("getCameraForLatLngBounds")) {
return null;
}
- return nativeGetCameraForLatLngBounds(latLngBounds);
+ return nativeGetCameraForLatLngBounds(
+ latLngBounds,
+ padding[1] / pixelRatio,
+ padding[0] / pixelRatio,
+ padding[3] / pixelRatio,
+ padding[2] / pixelRatio);
}
- public CameraPosition getCameraForGeometry(Geometry geometry, double bearing) {
+ public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) {
if (isDestroyedOn("getCameraForGeometry")) {
return null;
}
- return nativeGetCameraForGeometry(geometry, bearing);
+ return nativeGetCameraForGeometry(
+ geometry, bearing,
+ padding[1] / pixelRatio,
+ padding[0] / pixelRatio,
+ padding[3] / pixelRatio,
+ padding[2] / pixelRatio);
}
public void resetPosition() {
@@ -733,7 +743,7 @@ final class NativeMapView {
if (isDestroyedOn("addSource")) {
return;
}
- nativeAddSource(source.getNativePtr());
+ nativeAddSource(source, source.getNativePtr());
}
@Nullable
@@ -741,14 +751,19 @@ final class NativeMapView {
if (isDestroyedOn("removeSource")) {
return null;
}
- return nativeRemoveSourceById(sourceId);
+ Source source = getSource(sourceId);
+ if (source != null) {
+ return removeSource(source);
+ }
+ return null;
}
+ @Nullable
public Source removeSource(@NonNull Source source) {
if (isDestroyedOn("removeSource")) {
return null;
}
- nativeRemoveSource(source.getNativePtr());
+ nativeRemoveSource(source, source.getNativePtr());
return source;
}
@@ -757,20 +772,8 @@ final class NativeMapView {
return;
}
- // Check/correct config
- if (image.getConfig() != Bitmap.Config.ARGB_8888) {
- image = image.copy(Bitmap.Config.ARGB_8888, false);
- }
-
- // Get pixels
- ByteBuffer buffer = ByteBuffer.allocate(image.getByteCount());
- image.copyPixelsToBuffer(buffer);
-
// Determine pixel ratio
- float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity();
- float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT;
-
- nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array());
+ nativeAddImage(name, image, image.getDensity() / DisplayMetrics.DENSITY_DEFAULT);
}
public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) {
@@ -800,7 +803,7 @@ final class NativeMapView {
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates,
@Nullable String[] layerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
if (isDestroyedOn("queryRenderedFeatures")) {
return new ArrayList<>();
}
@@ -812,7 +815,7 @@ final class NativeMapView {
@NonNull
public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates,
@Nullable String[] layerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
if (isDestroyedOn("queryRenderedFeatures")) {
return new ArrayList<>();
}
@@ -907,9 +910,11 @@ final class NativeMapView {
private native LatLng nativeGetLatLng();
- private native CameraPosition nativeGetCameraForLatLngBounds(LatLngBounds latLngBounds);
+ private native CameraPosition nativeGetCameraForLatLngBounds(
+ LatLngBounds latLngBounds, double top, double left, double bottom, double right);
- private native CameraPosition nativeGetCameraForGeometry(Geometry geometry, double bearing);
+ private native CameraPosition nativeGetCameraForGeometry(
+ Geometry geometry, double bearing, double top, double left, double bottom, double right);
private native void nativeResetPosition();
@@ -1027,14 +1032,11 @@ final class NativeMapView {
private native Source nativeGetSource(String sourceId);
- private native void nativeAddSource(long nativeSourcePtr) throws CannotAddSourceException;
-
- private native Source nativeRemoveSourceById(String sourceId);
+ private native void nativeAddSource(Source source, long sourcePtr) throws CannotAddSourceException;
- 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);
+ private native void nativeAddImage(String name, Bitmap bitmap, float pixelRatio);
private native void nativeAddImages(Image[] images);
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 ae559189ad..f35355533d 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
@@ -9,6 +9,9 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.geometry.VisibleRegion;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* A projection is used to translate between on screen location and geographic coordinates on
* the surface of the Earth. Screen location is in screen pixels (not display pixels)
@@ -24,25 +27,17 @@ public class Projection {
this.contentPadding = new int[] {0, 0, 0, 0};
}
- void setContentPadding(int[] contentPadding, int[] userLocationViewPadding) {
+ void setContentPadding(int[] contentPadding) {
this.contentPadding = contentPadding;
-
- int[] padding = new int[] {
- contentPadding[0] + userLocationViewPadding[0],
- contentPadding[1] + userLocationViewPadding[1],
- contentPadding[2] + userLocationViewPadding[2],
- contentPadding[3] + userLocationViewPadding[3]
- };
-
- nativeMapView.setContentPadding(padding);
+ nativeMapView.setContentPadding(contentPadding);
}
int[] getContentPadding() {
return contentPadding;
}
- public void invalidateContentPadding(int[] userLocationViewPadding) {
- setContentPadding(contentPadding, userLocationViewPadding);
+ public void invalidateContentPadding() {
+ setContentPadding(contentPadding);
}
/**
@@ -103,14 +98,49 @@ public class Projection {
LatLng bottomRight = fromScreenLocation(new PointF(right, bottom));
LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom));
- return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
- new LatLngBounds.Builder()
- .include(topRight)
- .include(bottomLeft)
- .include(bottomRight)
- .include(topLeft)
- .build()
- );
+ // Map can be rotated, find correct LatLngBounds that encompasses the visible region (that might be rotated)
+ List<LatLng> boundsPoints = new ArrayList<>();
+ boundsPoints.add(topLeft);
+ boundsPoints.add(topRight);
+ boundsPoints.add(bottomRight);
+ boundsPoints.add(bottomLeft);
+
+ // order so that two most northern point are put first
+ while ((boundsPoints.get(0).getLatitude() < boundsPoints.get(3).getLatitude())
+ || (boundsPoints.get(1).getLatitude() < boundsPoints.get(2).getLatitude())) {
+ LatLng first = boundsPoints.remove(0);
+ boundsPoints.add(first);
+ }
+
+ double north = boundsPoints.get(0).getLatitude();
+ if (north < boundsPoints.get(1).getLatitude()) {
+ north = boundsPoints.get(1).getLatitude();
+ }
+
+ double south = boundsPoints.get(2).getLatitude();
+ if (south > boundsPoints.get(3).getLatitude()) {
+ south = boundsPoints.get(3).getLatitude();
+ }
+
+ double firstLon = boundsPoints.get(0).getLongitude();
+ double secondLon = boundsPoints.get(1).getLongitude();
+ double thridLon = boundsPoints.get(2).getLongitude();
+ double fourthLon = boundsPoints.get(3).getLongitude();
+
+ // if it does not go over the date line
+ if (secondLon > fourthLon && firstLon < thridLon) {
+ return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
+ LatLngBounds.from(north,
+ secondLon > thridLon ? secondLon : thridLon,
+ south,
+ firstLon < fourthLon ? firstLon : fourthLon));
+ } else {
+ return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight,
+ LatLngBounds.from(north,
+ secondLon < thridLon ? secondLon : thridLon,
+ south,
+ firstLon > fourthLon ? firstLon : fourthLon));
+ }
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java
new file mode 100644
index 0000000000..e6b93e4b91
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java
@@ -0,0 +1,48 @@
+package com.mapbox.mapboxsdk.maps;
+
+
+import com.mapbox.android.telemetry.MapboxTelemetry;
+import com.mapbox.android.telemetry.SessionInterval;
+import com.mapbox.android.telemetry.TelemetryEnabler;
+import com.mapbox.mapboxsdk.BuildConfig;
+import com.mapbox.mapboxsdk.Mapbox;
+
+public class Telemetry {
+ 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 Telemetry() {
+ telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(),
+ BuildConfig.MAPBOX_EVENTS_USER_AGENT);
+ TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences();
+ if (TelemetryEnabler.State.ENABLED.equals(telemetryState)) {
+ telemetry.enable();
+ }
+ }
+
+ public static void initialize() {
+ obtainTelemetry();
+ }
+
+ public static void updateDebugLoggingEnabled(boolean debugLoggingEnabled) {
+ TelemetryHolder.INSTANCE.telemetry.updateDebugLoggingEnabled(debugLoggingEnabled);
+ }
+
+ public static boolean updateSessionIdRotationInterval(SessionInterval interval) {
+ return TelemetryHolder.INSTANCE.telemetry.updateSessionIdRotationInterval(interval);
+ }
+
+ private static class TelemetryHolder {
+ private static final Telemetry INSTANCE = new Telemetry();
+ }
+
+ static MapboxTelemetry obtainTelemetry() {
+ return TelemetryHolder.INSTANCE.telemetry;
+ }
+}
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
deleted file mode 100644
index 81fd091c90..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java
+++ /dev/null
@@ -1,419 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.UiThread;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-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 timber.log.Timber;
-
-/**
- * Settings for the user location and bearing tracking of a MapboxMap.
- *
- * @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
- */
-@Deprecated
-public final class TrackingSettings {
-
- private final MyLocationView myLocationView;
- private final UiSettings uiSettings;
- private final FocalPointChangeListener focalPointChangedListener;
- private final CameraZoomInvalidator zoomInvalidator;
- private LocationEngine locationSource;
- private LocationEngineListener myLocationListener;
- private boolean locationChangeAnimationEnabled = true;
- private boolean isCustomLocationSource;
-
- private boolean myLocationEnabled;
- private boolean dismissLocationTrackingOnGesture = true;
- private boolean dismissBearingTrackingOnGesture = true;
-
- private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener;
- private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener;
-
- TrackingSettings(@NonNull MyLocationView myLocationView, UiSettings uiSettings,
- FocalPointChangeListener focalPointChangedListener, CameraZoomInvalidator zoomInvalidator) {
- this.myLocationView = myLocationView;
- this.focalPointChangedListener = focalPointChangedListener;
- this.uiSettings = uiSettings;
- this.zoomInvalidator = zoomInvalidator;
- }
-
- void initialise(MapboxMapOptions options) {
- locationSource = Mapbox.getLocationEngine();
- setMyLocationEnabled(options.getLocationEnabled());
- }
-
- void onSaveInstanceState(Bundle outState) {
- outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, getMyLocationTrackingMode());
- outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, getMyBearingTrackingMode());
- outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, isDismissLocationTrackingOnGesture());
- outState.putBoolean(MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, isDismissBearingTrackingOnGesture());
- outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, isMyLocationEnabled());
- outState.putBoolean(MapboxConstants.STATE_LOCATION_CHANGE_ANIMATION_ENABLED, isLocationChangeAnimationEnabled());
- outState.putBoolean(MapboxConstants.STATE_USING_CUSTOM_LOCATION_SOURCE, isCustomLocationSource());
- }
-
- void onRestoreInstanceState(Bundle savedInstanceState) {
- try {
- setMyLocationEnabled(
- savedInstanceState.getBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED),
- savedInstanceState.getBoolean(MapboxConstants.STATE_USING_CUSTOM_LOCATION_SOURCE)
- );
- } catch (SecurityException ignore) {
- // User did not accept location permissions
- }
- // noinspection ResourceType
- setMyLocationTrackingMode(savedInstanceState.getInt(
- MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE));
- // noinspection ResourceType
- setMyBearingTrackingMode(savedInstanceState.getInt(
- MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE));
- setDismissLocationTrackingOnGesture(savedInstanceState.getBoolean(
- MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, true));
- setDismissBearingTrackingOnGesture(savedInstanceState.getBoolean(
- MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, true));
- setLocationChangeAnimationEnabled(savedInstanceState.getBoolean(
- MapboxConstants.STATE_LOCATION_CHANGE_ANIMATION_ENABLED, true));
- }
-
- /**
- * <p>
- * Set the current my location tracking mode.
- * </p>
- * <p>
- * Will enable my location if not active.
- * </p>
- * See {@link MyLocationTracking} for different values.
- *
- * @param myLocationTrackingMode The location tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
- * @see MyLocationTracking
- */
- @UiThread
- public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- myLocationView.setLocationChangeAnimationEnabled(isLocationChangeAnimationEnabled());
- myLocationView.setMyLocationTrackingMode(myLocationTrackingMode);
-
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- zoomInvalidator.zoomTo(2.0);
- focalPointChangedListener.onFocalPointChanged(myLocationView.getCenter());
- } else {
- focalPointChangedListener.onFocalPointChanged(null);
- }
-
- if (onMyLocationTrackingModeChangeListener != null) {
- onMyLocationTrackingModeChangeListener.onMyLocationTrackingModeChange(myLocationTrackingMode);
- }
- }
-
- /**
- * Returns the current user location tracking mode.
- *
- * @return The current user location tracking mode.
- * One of the values from {@link MyLocationTracking.Mode}.
- * @see MyLocationTracking.Mode
- */
- @UiThread
- @MyLocationTracking.Mode
- public int getMyLocationTrackingMode() {
- return myLocationView.getMyLocationTrackingMode();
- }
-
- /**
- * <p>
- * Set the current my bearing tracking mode.
- * </p>
- * Shows the direction the user is heading.
- * <p>
- * When location tracking is disabled the direction of {@link MyLocationView} is rotated. When
- * location tracking is enabled the {@link MapView} is rotated based on the bearing value.
- * </p>
- * See {@link MyBearingTracking} for different values.
- *
- * @param myBearingTrackingMode The bearing tracking mode to be used.
- * @throws SecurityException if no suitable permission is present
- * @see MyBearingTracking
- */
- @UiThread
- public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- myLocationView.setMyBearingTrackingMode(myBearingTrackingMode);
- if (onMyBearingTrackingModeChangeListener != null) {
- onMyBearingTrackingModeChangeListener.onMyBearingTrackingModeChange(myBearingTrackingMode);
- }
- }
-
- /**
- * Returns the current user bearing tracking mode.
- * See {@link MyBearingTracking} for possible return values.
- *
- * @return the current user bearing tracking mode.
- * @see MyBearingTracking
- */
- @UiThread
- @MyBearingTracking.Mode
- public int getMyBearingTrackingMode() {
- return myLocationView.getMyBearingTrackingMode();
- }
-
- /**
- * Returns if all tracking modes will be dismissed when a gesture occurs.
- *
- * @return True to indicate that location and bearing tracking will be dismissed.
- */
- public boolean isAllDismissTrackingOnGesture() {
- return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture;
- }
-
- /**
- * Set the dismissal of the tracking modes if a gesture occurs.
- *
- * @param dismissTrackingOnGesture True to dismiss all the tracking modes.
- */
- public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) {
- dismissLocationTrackingOnGesture = dismissTrackingOnGesture;
- dismissBearingTrackingOnGesture = dismissTrackingOnGesture;
- }
-
- /**
- * Set the dismissal of the tracking modes if a gesture occurs.
- *
- * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode.
- */
- public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) {
- this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture;
- }
-
- /**
- * Returns if the location tracking will be disabled when a gesture occurs
- *
- * @return True if location tracking will be disabled.
- */
- public boolean isDismissLocationTrackingOnGesture() {
- return dismissLocationTrackingOnGesture;
- }
-
- /**
- * Set the dismissal of the bearing tracking modes if a gesture occurs.
- *
- * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode
- */
- public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) {
- this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture;
- }
-
- /**
- * Returns if bearing will disabled when a gesture occurs
- *
- * @return True if bearing tracking will be disabled
- */
- public boolean isDismissBearingTrackingOnGesture() {
- return dismissBearingTrackingOnGesture;
- }
-
- /**
- * Returns if location tracking is disabled
- *
- * @return True if location tracking is disabled.
- */
- public boolean isLocationTrackingDisabled() {
- return myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE;
- }
-
- /**
- * Returns if bearing tracking disabled
- *
- * @return True if bearing tracking is disabled.
- */
- public boolean isBearingTrackingDisabled() {
- return myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE;
- }
-
- /**
- * Returns if rotate gesture are currently enabled.
- *
- * @return True if rotate gestures are currently enabled.
- */
- public boolean isRotateGestureCurrentlyEnabled() {
- // rotate gestures are recognised if:
- // The user settings are enabled AND;
- // EITHER bearing tracking is dismissed on gesture OR there is no bearing tracking
- return uiSettings.isRotateGesturesEnabled()
- && (dismissBearingTrackingOnGesture
- || myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE
- || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE);
- }
-
- /**
- * Returns if scroll gesture are currently enabled.
- *
- * @return True if scroll gestures are currently enabled.
- */
- public boolean isScrollGestureCurrentlyEnabled() {
- return uiSettings.isScrollGesturesEnabled()
- && (dismissLocationTrackingOnGesture
- || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE);
- }
-
- /**
- * Returns whether location change animation is applied for {@link MyLocationTracking#TRACKING_FOLLOW}.
- *
- * @return True if animation is applied, false otherwise.
- */
- public boolean isLocationChangeAnimationEnabled() {
- return locationChangeAnimationEnabled;
- }
-
- /**
- * Set whether location change animation should be applied for {@link MyLocationTracking#TRACKING_FOLLOW}.
- *
- * @param locationChangeAnimationEnabled True if animation should be applied, false otherwise.
- */
- public void setLocationChangeAnimationEnabled(boolean locationChangeAnimationEnabled) {
- this.locationChangeAnimationEnabled = locationChangeAnimationEnabled;
-
- myLocationView.setLocationChangeAnimationEnabled(locationChangeAnimationEnabled);
- }
-
- /**
- * Reset the tracking modes as necessary. Location tracking is reset if the map center is changed and not from
- * location, bearing tracking if there is a rotation.
- *
- * @param translate true if translation
- * @param rotate true if rotation
- * @param isFromLocation true if from location
- */
- void resetTrackingModesIfRequired(boolean translate, boolean rotate, boolean isFromLocation) {
- // if tracking is on, and we should dismiss tracking with gestures, and this is a scroll action, turn tracking off
- if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture() && !isFromLocation) {
- setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- }
-
- // reset bearing tracking only on rotate
- if (rotate && !isBearingTrackingDisabled() && isDismissBearingTrackingOnGesture()) {
- setMyBearingTrackingMode(MyBearingTracking.NONE);
- }
- }
-
- /**
- * Reset the tracking modes as necessary. Animated camera position changes can reset the underlying tracking modes.
- *
- * @param currentCameraPosition the current camera position
- * @param targetCameraPosition the changed camera position
- * @param isFromLocation true if from location
- */
- void resetTrackingModesIfRequired(CameraPosition currentCameraPosition, CameraPosition targetCameraPosition,
- boolean isFromLocation) {
- if (currentCameraPosition.target != null) {
- resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false,
- isFromLocation);
- }
- }
-
- Location getMyLocation() {
- return myLocationView.getLocation();
- }
-
- void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) {
- if (listener != null) {
- myLocationListener = new LocationEngineListener() {
- @Override
- public void onConnected() {
- // Nothing
- }
-
- @Override
- public void onLocationChanged(Location location) {
- if (listener != null) {
- listener.onMyLocationChange(location);
- }
- }
- };
- locationSource.addLocationEngineListener(myLocationListener);
- } else {
- locationSource.removeLocationEngineListener(myLocationListener);
- myLocationListener = null;
- }
- }
-
- public boolean isCustomLocationSource() {
- return isCustomLocationSource;
- }
-
- void setOnMyLocationTrackingModeChangeListener(MapboxMap.OnMyLocationTrackingModeChangeListener listener) {
- this.onMyLocationTrackingModeChangeListener = listener;
- }
-
- void setOnMyBearingTrackingModeChangeListener(MapboxMap.OnMyBearingTrackingModeChangeListener listener) {
- this.onMyBearingTrackingModeChangeListener = listener;
- }
-
- MyLocationView getMyLocationView() {
- return myLocationView;
- }
-
-
- boolean isMyLocationEnabled() {
- return myLocationEnabled;
- }
-
- void setMyLocationEnabled(boolean locationEnabled) {
- setMyLocationEnabled(locationEnabled, isCustomLocationSource());
- }
-
- private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) {
- if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) {
- Timber.e("Could not activate user location tracking: "
- + "user did not accept the permission or permissions were not requested.");
- return;
- }
- myLocationEnabled = locationEnabled;
- this.isCustomLocationSource = isCustomLocationSource;
- myLocationView.setEnabled(locationEnabled, isCustomLocationSource);
- }
-
- void setLocationSource(LocationEngine locationSource) {
- if (this.locationSource != null && this.locationSource.equals(locationSource)) {
- // this source is already active
- return;
- }
-
- this.isCustomLocationSource = locationSource != null;
- if (locationSource == null) {
- locationSource = Mapbox.getLocationEngine();
- }
- this.locationSource = locationSource;
- myLocationView.setLocationSource(locationSource);
- }
-
- void update() {
- if (!myLocationView.isEnabled()) {
- return;
- }
- myLocationView.update();
- }
-
- void onStart() {
- myLocationView.onStart();
- }
-
- void onStop() {
- myLocationView.onStop();
- }
-
- interface CameraZoomInvalidator {
- void zoomTo(double zoomLevel);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
index 84a601039f..f76e54984b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java
@@ -12,7 +12,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdate;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
import timber.log.Timber;
@@ -30,8 +29,6 @@ final class Transform implements MapView.OnMapChangedListener {
private final NativeMapView mapView;
private final MarkerViewManager markerViewManager;
- private final TrackingSettings trackingSettings;
- private final MyLocationView myLocationView;
private final Handler handler = new Handler();
private CameraPosition cameraPosition;
@@ -41,12 +38,10 @@ final class Transform implements MapView.OnMapChangedListener {
private CameraChangeDispatcher cameraChangeDispatcher;
- Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings,
+ Transform(NativeMapView mapView, MarkerViewManager markerViewManager,
CameraChangeDispatcher cameraChangeDispatcher) {
this.mapView = mapView;
this.markerViewManager = markerViewManager;
- this.trackingSettings = trackingSettings;
- this.myLocationView = trackingSettings.getMyLocationView();
this.cameraChangeDispatcher = cameraChangeDispatcher;
}
@@ -73,9 +68,6 @@ final class Transform implements MapView.OnMapChangedListener {
@UiThread
void updateCameraPosition(@NonNull CameraPosition position) {
- if (myLocationView != null) {
- myLocationView.setCameraPosition(position);
- }
markerViewManager.setTilt((float) position.tilt);
}
@@ -103,7 +95,6 @@ final class Transform implements MapView.OnMapChangedListener {
final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, final MapboxMap.CancelableCallback callback) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
if (isValidCameraPosition(cameraPosition)) {
- trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false);
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom);
@@ -125,7 +116,6 @@ final class Transform implements MapView.OnMapChangedListener {
final MapboxMap.CancelableCallback callback, boolean isDismissable) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
if (isValidCameraPosition(cameraPosition)) {
- trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable);
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
@@ -143,7 +133,6 @@ final class Transform implements MapView.OnMapChangedListener {
final MapboxMap.CancelableCallback callback) {
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
if (isValidCameraPosition(cameraPosition)) {
- trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false);
cancelTransitions();
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
@@ -205,6 +194,8 @@ final class Transform implements MapView.OnMapChangedListener {
// cancel ongoing transitions
mapView.cancelTransitions();
+
+ cameraChangeDispatcher.onCameraIdle();
}
@UiThread
@@ -235,39 +226,21 @@ final class Transform implements MapView.OnMapChangedListener {
return mapView.getZoom();
}
- void zoom(boolean zoomIn, @NonNull PointF focalPoint) {
- CameraPosition cameraPosition = invalidateCameraPosition();
- if (cameraPosition != null) {
- int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1));
- setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION, false);
- } else {
- // we are not transforming, notify about being idle
- cameraChangeDispatcher.onCameraIdle();
- }
- }
-
- void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) {
- CameraPosition cameraPosition = invalidateCameraPosition();
- if (cameraPosition != null) {
- int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition);
- setZoom(newZoom, focalPoint, duration, false);
- } else {
- // we are not transforming, notify about being idle
- cameraChangeDispatcher.onCameraIdle();
- }
+ void zoomBy(double zoomAddition, @NonNull PointF focalPoint) {
+ setZoom(mapView.getZoom() + zoomAddition, focalPoint);
}
void setZoom(double zoom, @NonNull PointF focalPoint) {
- setZoom(zoom, focalPoint, 0, false);
+ setZoom(zoom, focalPoint, 0);
}
- void setZoom(double zoom, @NonNull PointF focalPoint, long duration, final boolean isAnimator) {
+ void setZoom(double zoom, @NonNull PointF focalPoint, final long duration) {
if (mapView != null) {
mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
@Override
public void onMapChanged(int change) {
if (change == MapView.REGION_DID_CHANGE_ANIMATED) {
- if (!isAnimator) {
+ if (duration > 0) {
cameraChangeDispatcher.onCameraIdle();
}
mapView.removeOnMapChangedListener(this);
@@ -297,23 +270,14 @@ final class Transform implements MapView.OnMapChangedListener {
}
void setBearing(double bearing) {
- if (myLocationView != null) {
- myLocationView.setBearing(bearing);
- }
mapView.setBearing(bearing);
}
void setBearing(double bearing, float focalX, float focalY) {
- if (myLocationView != null) {
- myLocationView.setBearing(bearing);
- }
mapView.setBearing(bearing, focalX, focalY);
}
void setBearing(double bearing, float focalX, float focalY, long duration) {
- if (myLocationView != null) {
- myLocationView.setBearing(bearing);
- }
mapView.setBearing(bearing, focalX, focalY, duration);
}
@@ -335,9 +299,6 @@ final class Transform implements MapView.OnMapChangedListener {
}
void setTilt(Double pitch) {
- if (myLocationView != null) {
- myLocationView.setTilt(pitch);
- }
markerViewManager.setTilt(pitch.floatValue());
mapView.setPitch(pitch, 0);
}
@@ -361,10 +322,6 @@ final class Transform implements MapView.OnMapChangedListener {
}
}
- void zoomBy(double z, float x, float y) {
- mapView.setZoom(mapView.getZoom() + z, new PointF(x, y), 0);
- }
-
void moveBy(double offsetX, double offsetY, long duration) {
if (duration > 0) {
mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() {
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 44f106bdd5..c1daebbe52 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];
@@ -44,21 +45,23 @@ public final class UiSettings {
private float pixelRatio;
private boolean rotateGesturesEnabled = true;
- private boolean rotateGestureChangeAllowed = true;
private boolean tiltGesturesEnabled = true;
- private boolean tiltGestureChangeAllowed = true;
private boolean zoomGesturesEnabled = true;
- private boolean zoomGestureChangeAllowed = true;
private boolean scrollGesturesEnabled = true;
- private boolean scrollGestureChangeAllowed = true;
private boolean zoomControlsEnabled;
private boolean doubleTapGesturesEnabled = true;
- private boolean doubleTapGestureChangeAllowed = true;
+
+ private boolean scaleVelocityAnimationEnabled = true;
+ private boolean rotateVelocityAnimationEnabled = true;
+ private boolean flingVelocityAnimationEnabled = true;
+
+ private boolean increaseRotateThresholdWhenScaling = true;
+ private boolean increaseScaleThresholdWhenRotating = true;
private boolean deselectMarkersOnTap = true;
@@ -107,42 +110,39 @@ public final class UiSettings {
private void initialiseGestures(MapboxMapOptions options) {
setZoomGesturesEnabled(options.getZoomGesturesEnabled());
- setZoomGestureChangeAllowed(options.getZoomGesturesEnabled());
setScrollGesturesEnabled(options.getScrollGesturesEnabled());
- setScrollGestureChangeAllowed(options.getScrollGesturesEnabled());
setRotateGesturesEnabled(options.getRotateGesturesEnabled());
- setRotateGestureChangeAllowed(options.getRotateGesturesEnabled());
setTiltGesturesEnabled(options.getTiltGesturesEnabled());
- setTiltGestureChangeAllowed(options.getTiltGesturesEnabled());
setZoomControlsEnabled(options.getZoomControlsEnabled());
setDoubleTapGesturesEnabled(options.getDoubleTapGesturesEnabled());
- setDoubleTapGestureChangeAllowed(options.getDoubleTapGesturesEnabled());
}
private void saveGestures(Bundle outState) {
outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED, isZoomGesturesEnabled());
- outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE, isZoomGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED, isScrollGesturesEnabled());
- outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE, isScrollGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED, isRotateGesturesEnabled());
- outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE, isRotateGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED, isTiltGesturesEnabled());
- outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE, isTiltGestureChangeAllowed());
outState.putBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED, isDoubleTapGesturesEnabled());
- outState.putBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED_CHANGE, isDoubleTapGestureChangeAllowed());
+ outState.putBoolean(MapboxConstants.STATE_SCALE_ANIMATION_ENABLED, isScaleVelocityAnimationEnabled());
+ outState.putBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED, isRotateVelocityAnimationEnabled());
+ outState.putBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED, isFlingVelocityAnimationEnabled());
+ outState.putBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD, isIncreaseRotateThresholdWhenScaling());
+ outState.putBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD, isIncreaseScaleThresholdWhenRotating());
}
private void restoreGestures(Bundle savedInstanceState) {
setZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED));
- setZoomGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE));
setScrollGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED));
- setScrollGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE));
setRotateGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED));
- setRotateGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE));
setTiltGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED));
- setTiltGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE));
setDoubleTapGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED));
- setDoubleTapGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED_CHANGE));
+ setScaleVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCALE_ANIMATION_ENABLED));
+ setRotateVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED));
+ setFlingVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED));
+ setIncreaseRotateThresholdWhenScaling(
+ savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD));
+ setIncreaseScaleThresholdWhenRotating(
+ savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD));
}
private void initialiseCompass(MapboxMapOptions options, Resources resources) {
@@ -535,6 +535,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.
@@ -635,9 +657,7 @@ public final class UiSettings {
* @param rotateGesturesEnabled If true, rotating is enabled.
*/
public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) {
- if (rotateGestureChangeAllowed) {
- this.rotateGesturesEnabled = rotateGesturesEnabled;
- }
+ this.rotateGesturesEnabled = rotateGesturesEnabled;
}
/**
@@ -649,14 +669,6 @@ public final class UiSettings {
return rotateGesturesEnabled;
}
- void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) {
- this.rotateGestureChangeAllowed = rotateGestureChangeAllowed;
- }
-
- boolean isRotateGestureChangeAllowed() {
- return rotateGestureChangeAllowed;
- }
-
/**
* <p>
* Changes whether the user may tilt the map.
@@ -670,9 +682,8 @@ public final class UiSettings {
* @param tiltGesturesEnabled If true, tilting is enabled.
*/
public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) {
- if (tiltGestureChangeAllowed) {
- this.tiltGesturesEnabled = tiltGesturesEnabled;
- }
+ this.tiltGesturesEnabled = tiltGesturesEnabled;
+
}
/**
@@ -684,14 +695,6 @@ public final class UiSettings {
return tiltGesturesEnabled;
}
- void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) {
- this.tiltGestureChangeAllowed = tiltGestureChangeAllowed;
- }
-
- boolean isTiltGestureChangeAllowed() {
- return tiltGestureChangeAllowed;
- }
-
/**
* <p>
* Changes whether the user may zoom the map.
@@ -705,9 +708,7 @@ public final class UiSettings {
* @param zoomGesturesEnabled If true, zooming is enabled.
*/
public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) {
- if (zoomGestureChangeAllowed) {
- this.zoomGesturesEnabled = zoomGesturesEnabled;
- }
+ this.zoomGesturesEnabled = zoomGesturesEnabled;
}
/**
@@ -719,14 +720,6 @@ public final class UiSettings {
return zoomGesturesEnabled;
}
- void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) {
- this.zoomGestureChangeAllowed = zoomGestureChangeAllowed;
- }
-
- boolean isZoomGestureChangeAllowed() {
- return zoomGestureChangeAllowed;
- }
-
/**
* <p>
* Sets whether the zoom controls are enabled.
@@ -765,9 +758,7 @@ public final class UiSettings {
* @param doubleTapGesturesEnabled If true, zooming with a double tap is enabled.
*/
public void setDoubleTapGesturesEnabled(boolean doubleTapGesturesEnabled) {
- if (doubleTapGestureChangeAllowed) {
- this.doubleTapGesturesEnabled = doubleTapGesturesEnabled;
- }
+ this.doubleTapGesturesEnabled = doubleTapGesturesEnabled;
}
/**
@@ -779,14 +770,6 @@ public final class UiSettings {
return doubleTapGesturesEnabled;
}
- void setDoubleTapGestureChangeAllowed(boolean doubleTapGestureChangeAllowed) {
- this.doubleTapGestureChangeAllowed = doubleTapGestureChangeAllowed;
- }
-
- boolean isDoubleTapGestureChangeAllowed() {
- return doubleTapGestureChangeAllowed;
- }
-
private void restoreDeselectMarkersOnTap(Bundle savedInstanceState) {
setDeselectMarkersOnTap(savedInstanceState.getBoolean(MapboxConstants.STATE_DESELECT_MARKER_ON_TAP));
}
@@ -828,9 +811,7 @@ public final class UiSettings {
* @param scrollGesturesEnabled If true, scrolling is enabled.
*/
public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) {
- if (scrollGestureChangeAllowed) {
- this.scrollGesturesEnabled = scrollGesturesEnabled;
- }
+ this.scrollGesturesEnabled = scrollGesturesEnabled;
}
/**
@@ -842,12 +823,105 @@ public final class UiSettings {
return scrollGesturesEnabled;
}
- void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) {
- this.scrollGestureChangeAllowed = scrollGestureChangeAllowed;
+ /**
+ * Returns whether scale velocity animation should execute after users finishes a gesture.
+ *
+ * @return If true, scale velocity animation is enabled.
+ */
+ public boolean isScaleVelocityAnimationEnabled() {
+ return scaleVelocityAnimationEnabled;
+ }
+
+ /**
+ * Set whether scale velocity animation should execute after users finishes a gesture. True by default.
+ *
+ * @param scaleVelocityAnimationEnabled If true, scale velocity animation will be enabled.
+ */
+ public void setScaleVelocityAnimationEnabled(boolean scaleVelocityAnimationEnabled) {
+ this.scaleVelocityAnimationEnabled = scaleVelocityAnimationEnabled;
+ }
+
+ /**
+ * Returns whether rotate velocity animation should execute after users finishes a gesture.
+ *
+ * @return If true, rotate velocity animation is enabled.
+ */
+ public boolean isRotateVelocityAnimationEnabled() {
+ return rotateVelocityAnimationEnabled;
+ }
+
+ /**
+ * Set whether rotate velocity animation should execute after users finishes a gesture. True by default.
+ *
+ * @param rotateVelocityAnimationEnabled If true, rotate velocity animation will be enabled.
+ */
+ public void setRotateVelocityAnimationEnabled(boolean rotateVelocityAnimationEnabled) {
+ this.rotateVelocityAnimationEnabled = rotateVelocityAnimationEnabled;
+ }
+
+ /**
+ * Returns whether fling velocity animation should execute after users finishes a gesture.
+ *
+ * @return If true, fling velocity animation is enabled.
+ */
+ public boolean isFlingVelocityAnimationEnabled() {
+ return flingVelocityAnimationEnabled;
+ }
+
+ /**
+ * Set whether fling velocity animation should execute after users finishes a gesture. True by default.
+ *
+ * @param flingVelocityAnimationEnabled If true, fling velocity animation will be enabled.
+ */
+ public void setFlingVelocityAnimationEnabled(boolean flingVelocityAnimationEnabled) {
+ this.flingVelocityAnimationEnabled = flingVelocityAnimationEnabled;
+ }
+
+ /**
+ * Set whether all velocity animations should execute after users finishes a gesture.
+ *
+ * @param allVelocityAnimationsEnabled If true, all velocity animations will be enabled.
+ */
+ public void setAllVelocityAnimationsEnabled(boolean allVelocityAnimationsEnabled) {
+ setScaleVelocityAnimationEnabled(allVelocityAnimationsEnabled);
+ setRotateVelocityAnimationEnabled(allVelocityAnimationsEnabled);
+ setFlingVelocityAnimationEnabled(allVelocityAnimationsEnabled);
+ }
+
+ /**
+ * Returns whether rotation threshold should be increase whenever scale is detected.
+ *
+ * @return If true, rotation threshold will be increased.
+ */
+ public boolean isIncreaseRotateThresholdWhenScaling() {
+ return increaseRotateThresholdWhenScaling;
+ }
+
+ /**
+ * Set whether rotation threshold should be increase whenever scale is detected.
+ *
+ * @param increaseRotateThresholdWhenScaling If true, rotation threshold will be increased.
+ */
+ public void setIncreaseRotateThresholdWhenScaling(boolean increaseRotateThresholdWhenScaling) {
+ this.increaseRotateThresholdWhenScaling = increaseRotateThresholdWhenScaling;
}
- boolean isScrollGestureChangeAllowed() {
- return scrollGestureChangeAllowed;
+ /**
+ * Returns whether scale threshold should be increase whenever rotation is detected.
+ *
+ * @return If true, scale threshold will be increased.
+ */
+ public boolean isIncreaseScaleThresholdWhenRotating() {
+ return increaseScaleThresholdWhenRotating;
+ }
+
+ /**
+ * set whether scale threshold should be increase whenever rotation is detected.
+ *
+ * @param increaseScaleThresholdWhenRotating If true, scale threshold will be increased.
+ */
+ public void setIncreaseScaleThresholdWhenRotating(boolean increaseScaleThresholdWhenRotating) {
+ this.increaseScaleThresholdWhenRotating = increaseScaleThresholdWhenRotating;
}
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java
index 247ffea906..46238ee789 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java
@@ -54,6 +54,17 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
@SuppressWarnings("JavadocReference")
private static final int EGL_OPENGL_ES2_BIT = 0x0004;
+ private boolean translucentSurface;
+
+ public EGLConfigChooser() {
+ this(false);
+ }
+
+ public EGLConfigChooser(boolean translucentSurface) {
+ super();
+ this.translucentSurface = translucentSurface;
+ }
+
@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] configAttribs = getConfigAttributes();
@@ -274,7 +285,7 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
- EGL_ALPHA_SIZE, 0,
+ EGL_ALPHA_SIZE, translucentSurface ? 8 : 0,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
(emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java
new file mode 100644
index 0000000000..b14b302652
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android EGL API classes.
+ */
+package com.mapbox.mapboxsdk.maps.renderer.egl;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java
new file mode 100644
index 0000000000..aefcffef42
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android GLSurfaceView API classes.
+ */
+package com.mapbox.mapboxsdk.maps.renderer.glsurfaceview;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java
new file mode 100644
index 0000000000..f5d8021ea1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android Renderer API classes.
+ */
+package com.mapbox.mapboxsdk.maps.renderer;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java
index dcc95217ff..ad25dea0d3 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java
@@ -17,17 +17,22 @@ import javax.microedition.khronos.opengles.GL10;
*/
public class TextureViewMapRenderer extends MapRenderer {
private TextureViewRenderThread renderThread;
+ private boolean translucentSurface;
/**
* Create a {@link MapRenderer} for the given {@link TextureView}
*
- * @param context the current Context
- * @param textureView the TextureView
+ * @param context the current Context
+ * @param textureView the TextureView
+ * @param localIdeographFontFamily the local font family
+ * @param translucentSurface the translucency flag
*/
public TextureViewMapRenderer(@NonNull Context context,
@NonNull TextureView textureView,
- String localIdeographFontFamily) {
+ String localIdeographFontFamily,
+ boolean translucentSurface) {
super(context, localIdeographFontFamily);
+ this.translucentSurface = translucentSurface;
renderThread = new TextureViewRenderThread(textureView, this);
renderThread.start();
}
@@ -95,4 +100,8 @@ public class TextureViewMapRenderer extends MapRenderer {
public void onDestroy() {
renderThread.onDestroy();
}
+
+ public boolean isTranslucentSurface() {
+ return translucentSurface;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
index 1e76ffe3fb..4bba160993 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java
@@ -53,9 +53,10 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
*/
@UiThread
TextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) {
+ textureView.setOpaque(!mapRenderer.isTranslucentSurface());
textureView.setSurfaceTextureListener(this);
this.mapRenderer = mapRenderer;
- this.eglHolder = new EGLHolder(new WeakReference<>(textureView));
+ this.eglHolder = new EGLHolder(new WeakReference<>(textureView), mapRenderer.isTranslucentSurface());
}
// SurfaceTextureListener methods
@@ -324,6 +325,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
private static class EGLHolder {
private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
private final WeakReference<TextureView> textureViewWeakRef;
+ private boolean translucentSurface;
private EGL10 egl;
private EGLConfig eglConfig;
@@ -331,8 +333,9 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
private EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
- EGLHolder(WeakReference<TextureView> textureViewWeakRef) {
+ EGLHolder(WeakReference<TextureView> textureViewWeakRef, boolean translucentSurface) {
this.textureViewWeakRef = textureViewWeakRef;
+ this.translucentSurface = translucentSurface;
}
void prepare() {
@@ -357,7 +360,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu
eglConfig = null;
eglContext = EGL10.EGL_NO_CONTEXT;
} else if (eglContext == EGL10.EGL_NO_CONTEXT) {
- eglConfig = new EGLConfigChooser().chooseConfig(egl, eglDisplay);
+ eglConfig = new EGLConfigChooser(translucentSurface).chooseConfig(egl, eglDisplay);
int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java
new file mode 100644
index 0000000000..d3585d41f9
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android TextureView API classes.
+ */
+package com.mapbox.mapboxsdk.maps.renderer.textureview;
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..fb2c70cb73 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
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.maps.widgets;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
@@ -22,6 +23,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap;
* use {@link com.mapbox.mapboxsdk.maps.UiSettings}.
* </p>
*/
+@SuppressLint("AppCompatCustomView")
public final class CompassView extends ImageView implements Runnable {
public static final long TIME_WAIT_IDLE = 500;
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
deleted file mode 100644
index 1cdd91028d..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java
+++ /dev/null
@@ -1,1104 +0,0 @@
-package com.mapbox.mapboxsdk.maps.widgets;
-
-import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.graphics.Camera;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.location.Location;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.os.SystemClock;
-import android.support.annotation.ColorInt;
-import android.support.annotation.FloatRange;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-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 java.lang.ref.WeakReference;
-
-import timber.log.Timber;
-
-/**
- * UI element overlaid on a map to show the user's location.
- * <p>
- * Use {@link MyLocationViewSettings} to manipulate the state of this view.
- * </p>
- *
- * @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
- */
-@Deprecated
-public class MyLocationView extends View {
-
- private static final int UNDEFINED_TINT_COLOR = -1;
- private MyLocationBehavior myLocationBehavior;
- private MapboxMap mapboxMap;
-
- private Projection projection;
- private float[] projectedCoordinate = new float[2];
- private float projectedX;
- private float projectedY;
-
- private float contentPaddingX;
- private float contentPaddingY;
-
- private LatLng latLng;
- private Location location;
- private LocationEngine locationEngine;
- private long locationUpdateTimestamp;
- private float previousDirection;
-
- private float accuracy;
- private Paint accuracyPaint;
- private float accuracyThreshold;
-
- private ValueAnimator locationChangeAnimator;
- private ValueAnimator accuracyAnimator;
- private ValueAnimator directionAnimator;
- private boolean locationChangeAnimationEnabled = true;
-
- private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener =
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidate();
- }
- };
-
- private Drawable foregroundDrawable;
- private Drawable foregroundBearingDrawable;
- private Drawable backgroundDrawable;
-
- private Rect foregroundBounds;
- private Rect backgroundBounds;
-
- private int backgroundOffsetLeft;
- private int backgroundOffsetTop;
- private int backgroundOffsetRight;
- private int backgroundOffsetBottom;
-
- private Matrix matrix;
- private Camera camera;
- private PointF screenLocation;
-
- // camera vars
- private double tilt;
- private double bearing;
- private float magneticHeading;
-
- // Controls the compass update rate in milliseconds
- private static final int COMPASS_UPDATE_RATE_MS = 500;
-
- @MyLocationTracking.Mode
- private int myLocationTrackingMode;
-
- @MyBearingTracking.Mode
- private int myBearingTrackingMode;
-
- private GpsLocationListener userLocationListener;
- private CompassListener compassListener;
-
- public MyLocationView(Context context) {
- super(context);
- init(context);
- }
-
- public MyLocationView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context);
- }
-
- public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init(context);
- }
-
- private void init(Context context) {
- if (isInEditMode()) {
- return;
- }
-
- setEnabled(false);
-
- // setup LayoutParams
- ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- setLayoutParams(lp);
-
- matrix = new Matrix();
- camera = new Camera();
- camera.setLocation(0, 0, -1000);
- accuracyPaint = new Paint();
-
- myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE);
- compassListener = new CompassListener(context);
- }
-
- @Deprecated
- public void init(LocationEngine locationSource) {
- this.locationEngine = locationSource;
- }
-
- /**
- * Set the foreground drawable, for internal use only.
- *
- * @param defaultDrawable The drawable shown when showing this view
- * @param bearingDrawable The drawable shown when tracking of bearing is enabled
- */
- public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) {
- if (defaultDrawable == null) {
- return;
- }
-
- if (bearingDrawable == null) {
- // if user only provided one resource
- // use same for bearing mode
- bearingDrawable = defaultDrawable.getConstantState().newDrawable();
- }
-
- if (backgroundDrawable == null) {
- // if the user didn't provide a background resource we will use the foreground resource instead,
- // we need to create a new drawable to handle tinting correctly
- backgroundDrawable = defaultDrawable.getConstantState().newDrawable();
- }
-
- if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth()
- || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) {
- throw new RuntimeException("The dimensions from location and bearing drawables should be match");
- }
-
- foregroundDrawable = defaultDrawable;
- foregroundBearingDrawable = bearingDrawable;
-
- invalidateBounds();
- }
-
- /**
- * Set the foreground drawable tint, for internal use only.
- *
- * @param color The color to tint the drawable with
- */
- public final void setForegroundDrawableTint(@ColorInt int color) {
- applyDrawableTint(foregroundDrawable, color);
- applyDrawableTint(foregroundBearingDrawable, color);
- invalidate();
- }
-
- /**
- * Set the shadow drawable, for internal use only.
- *
- * @param drawable The drawable shown as shadow
- */
- public final void setShadowDrawable(Drawable drawable) {
- setShadowDrawable(drawable, 0, 0, 0, 0);
- }
-
- /**
- * Set the shadow drawable with some additional offset.
- *
- * @param drawable The drawable shown as shadow
- * @param left The left offset margin
- * @param top The top offset margin
- * @param right The right offset margin
- * @param bottom The bottom offset margin
- */
- public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) {
- if (drawable != null) {
- backgroundDrawable = drawable;
- }
-
- backgroundOffsetLeft = left;
- backgroundOffsetTop = top;
- backgroundOffsetRight = right;
- backgroundOffsetBottom = bottom;
-
- invalidateBounds();
- }
-
- /**
- * Set the shadow drawable tint color, for internal use only.
- *
- * @param color The tint color to apply
- */
- public final void setShadowDrawableTint(@ColorInt int color) {
- if (backgroundDrawable == null) {
- return;
- }
- applyDrawableTint(backgroundDrawable, color);
- invalidate();
- }
-
- /**
- * Set the accuracy tint color, for internal use only.
- *
- * @param color The tint color to apply
- */
- public final void setAccuracyTint(@ColorInt int color) {
- int alpha = accuracyPaint.getAlpha();
- accuracyPaint.setColor(color);
- accuracyPaint.setAlpha(alpha);
- invalidate();
- }
-
- /**
- * Set the accuracy alpha value, for internal use only.
- *
- * @param alpha The alpha accuracy value to apply
- */
- public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) {
- accuracyPaint.setAlpha(alpha);
- invalidate();
- }
-
- private void invalidateBounds() {
- if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) {
- return;
- }
-
- int backgroundWidth = backgroundDrawable.getIntrinsicWidth();
- int backgroundHeight = backgroundDrawable.getIntrinsicHeight();
- int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight;
- int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom;
- backgroundBounds = new Rect(-backgroundWidth / 2 + horizontalOffset,
- -backgroundHeight / 2 + verticalOffset, backgroundWidth / 2 + horizontalOffset, backgroundHeight / 2
- + verticalOffset);
- backgroundDrawable.setBounds(backgroundBounds);
-
- int foregroundWidth = foregroundDrawable.getIntrinsicWidth();
- int foregroundHeight = foregroundDrawable.getIntrinsicHeight();
- foregroundBounds = new Rect(-foregroundWidth / 2, -foregroundHeight / 2, foregroundWidth / 2, foregroundHeight / 2);
- foregroundDrawable.setBounds(foregroundBounds);
- foregroundBearingDrawable.setBounds(foregroundBounds);
-
- // invoke a new draw
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null
- || screenLocation == null) {
- // Not ready yet
- return;
- }
-
- final PointF pointF = screenLocation;
- float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude());
- float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel;
-
- // reset
- matrix.reset();
- projectedCoordinate[0] = 0;
- projectedCoordinate[1] = 0;
-
- // put camera in position
- camera.save();
- camera.rotate((float) tilt, 0, 0);
- camera.getMatrix(matrix);
-
- if (myBearingTrackingMode != MyBearingTracking.NONE && directionAnimator != null) {
- matrix.preRotate((Float) directionAnimator.getAnimatedValue());
- }
-
- matrix.preTranslate(0, contentPaddingY);
- matrix.postTranslate(pointF.x, pointF.y - contentPaddingY);
-
- // concat our matrix on canvas
- canvas.concat(matrix);
-
- // calculate focal point
- matrix.mapPoints(projectedCoordinate);
- projectedX = pointF.x - projectedCoordinate[0];
- projectedY = pointF.y - projectedCoordinate[1];
-
- // restore orientation from camera
- camera.restore();
-
- // draw circle
- canvas.drawCircle(0, 0, accuracyPixels, accuracyPaint);
-
- // draw shadow
- if (backgroundDrawable != null) {
- backgroundDrawable.draw(canvas);
- }
-
- // draw foreground
- if (myBearingTrackingMode == MyBearingTracking.NONE) {
- if (foregroundDrawable != null) {
- foregroundDrawable.draw(canvas);
- }
- } else if (foregroundBearingDrawable != null && foregroundBounds != null) {
- if (myBearingTrackingMode == MyBearingTracking.GPS
- || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING
- || compassListener.isSensorAvailable()) {
- foregroundBearingDrawable.draw(canvas);
- } else {
- // We are tracking MyBearingTracking.COMPASS, but sensor is not available.
- foregroundDrawable.draw(canvas);
- }
- }
- }
-
- /**
- * Set the tilt value, for internal use only.
- *
- * @param tilt The tilt to apply
- */
- public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) {
- this.tilt = tilt;
- invalidate();
- }
-
- /**
- * Set the bearing value, for internal use only.
- *
- * @param bearing The bearing to apply
- */
- public void setBearing(double bearing) {
- this.bearing = bearing;
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- if (myBearingTrackingMode == MyBearingTracking.GPS
- || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) {
- if (location != null) {
- setCompass(location.getBearing() - bearing);
- }
- } else if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) {
- setCompass(magneticHeading - bearing);
- }
- }
- }
-
- /**
- * Set the bearing and tilt from a camera position, for internal use only.
- *
- * @param position The camera position to extract bearing and tilt from
- */
- public void setCameraPosition(CameraPosition position) {
- if (position != null) {
- setBearing(position.bearing);
- setTilt(position.tilt);
- }
- }
-
- /**
- * Called when the hosting activity is starting, for internal use only.
- */
- public void onStart() {
- if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) {
- compassListener.onResume();
- }
- if (isEnabled()) {
- toggleGps(true);
- }
- }
-
- /**
- * Called when the hosting activity is stopping, for internal use only.
- */
- public void onStop() {
- compassListener.onPause();
- toggleGps(false);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- // cleanup to prevent memory leaks
- if (locationChangeAnimator != null) {
- locationChangeAnimator.cancel();
- locationChangeAnimator = null;
- }
-
- if (accuracyAnimator != null) {
- accuracyAnimator.cancel();
- accuracyAnimator = null;
- }
-
- if (directionAnimator != null) {
- directionAnimator.cancel();
- directionAnimator = null;
- }
-
- if (userLocationListener != null) {
- locationEngine.removeLocationEngineListener(userLocationListener);
- locationEngine = null;
- userLocationListener = null;
- }
- }
-
- /**
- * Update current locationstate.
- */
- public void update() {
- if (isEnabled()) {
- myLocationBehavior.invalidate();
- } else {
- setVisibility(View.INVISIBLE);
- }
- }
-
- // TODO refactor MapboxMap out
- public void setMapboxMap(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- this.projection = mapboxMap.getProjection();
- }
-
- /**
- * Set the enabled state, for internal use only.
- *
- * @param enabled The value to set the state to
- */
- @Override
- public void setEnabled(boolean enabled) {
- setEnabled(enabled, false);
- }
-
- /**
- * Set the enabled state, for internal use only.
- *
- * @param enabled The value to set the state to
- * @param isCustomLocationEngine Flag handling for handling user provided custom location engine
- */
- public void setEnabled(boolean enabled, boolean isCustomLocationEngine) {
- super.setEnabled(enabled);
- setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
- toggleGps(enabled, isCustomLocationEngine);
- }
-
- /**
- * Save the view instance state, for internal use only.
- *
- * @return the marshaled representation of the view state
- */
- @Override
- protected Parcelable onSaveInstanceState() {
- Bundle bundle = new Bundle();
- bundle.putParcelable("superState", super.onSaveInstanceState());
- bundle.putDouble("tilt", tilt);
- return bundle;
- }
-
- /**
- * Restore the view instance state, for internal use only.
- *
- * @param state the marshalled representation of the state to restore
- */
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (state instanceof Bundle) {
- Bundle bundle = (Bundle) state;
- tilt = bundle.getDouble("tilt");
- state = bundle.getParcelable("superState");
- }
- super.onRestoreInstanceState(state);
- }
-
- private void toggleGps(boolean enableGps) {
- toggleGps(enableGps, mapboxMap != null
- && mapboxMap.getTrackingSettings().isCustomLocationSource());
- }
-
- /**
- * Enabled / Disable GPS location updates along with updating the UI, for internal use only.
- *
- * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled
- */
- private void toggleGps(boolean enableGps, boolean isCustomLocationEngine) {
- if (enableGps) {
- if (locationEngine == null) {
- if (!isCustomLocationEngine) {
- locationEngine = Mapbox.getLocationEngine();
- } else {
- return;
- }
- }
-
- if (userLocationListener == null) {
- userLocationListener = new GpsLocationListener(this, locationEngine);
- }
-
- locationEngine.addLocationEngineListener(userLocationListener);
- locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
- locationEngine.activate();
- } else {
- if (locationEngine == null) {
- return;
- }
- // Disable location and user dot
- location = null;
- locationEngine.removeLocationEngineListener(userLocationListener);
- locationEngine.removeLocationUpdates();
- locationEngine.deactivate();
- restoreLocationEngine();
- }
- }
-
- /**
- * Get the current location.
- *
- * @return the current location
- */
- public Location getLocation() {
- return location;
- }
-
- /**
- * Set the current location, for internal use only.
- *
- * @param location The current location
- */
- public void setLocation(Location location) {
- if (location == null) {
- this.location = null;
- return;
- }
-
- this.location = location;
- myLocationBehavior.updateLatLng(location);
-
- if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS
- || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING)
- && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) {
- setBearing(mapboxMap.getCameraPosition().bearing);
- }
- }
-
- /**
- * Set location change animation enabled, for internal use only.
- *
- * @param locationChangeAnimationEnabled True if location changes are animated
- */
- public void setLocationChangeAnimationEnabled(boolean locationChangeAnimationEnabled) {
- this.locationChangeAnimationEnabled = locationChangeAnimationEnabled;
- }
-
- /**
- * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value.
- * For internal use only.
- *
- * @param accuracyThreshold Value below which circle won't be displayed
- */
- public void setAccuracyThreshold(float accuracyThreshold) {
- this.accuracyThreshold = accuracyThreshold;
- }
-
- /**
- * Set the bearing tracking mode, for internal use only.
- *
- * @param myBearingTrackingMode The bearing tracking mode
- */
- public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) {
- this.myBearingTrackingMode = myBearingTrackingMode;
- if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) {
- compassListener.onResume();
- } else {
- compassListener.onPause();
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW
- && myBearingTrackingMode == MyBearingTracking.GPS) {
- // always face north
- setCompass(0);
- } else {
- myLocationBehavior.invalidate();
- }
- }
- invalidate();
- }
-
- /**
- * Set the location tracking mode, for internla use only.
- *
- * @param myLocationTrackingMode The location tracking mode
- */
- public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) {
- MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory();
- myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode);
-
- if (location != null) {
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- // center map directly
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location)));
- } else {
- // do not use interpolated location from tracking mode
- latLng = null;
- }
- myLocationBehavior.updateLatLng(location);
- }
-
- this.myLocationTrackingMode = myLocationTrackingMode;
- invalidate();
- }
-
- /**
- * Get the location tracking mode, for internal use only.
- *
- * @return The location tracking mode
- */
- @MyLocationTracking.Mode
- public int getMyLocationTrackingMode() {
- return myLocationTrackingMode;
- }
-
-
- /**
- * Get the bearing tracking mode, for internal use only.
- *
- * @return the bearing tracking mode
- */
- @MyBearingTracking.Mode
- public int getMyBearingTrackingMode() {
- return myBearingTrackingMode;
- }
-
- /**
- * Set the compass bearing value, for internal use only.
- *
- * @param bearing The compas bearing value
- */
- private void setCompass(double bearing) {
- setCompass(bearing, 0 /* no animation */);
- }
-
- private void setCompass(double bearing, long duration) {
- float oldDir = previousDirection;
- if (directionAnimator != null) {
- oldDir = (Float) directionAnimator.getAnimatedValue();
- directionAnimator.end();
- directionAnimator = null;
- }
-
- float newDir = (float) bearing;
- float diff = oldDir - newDir;
- if (diff > 180.0f) {
- newDir += 360.0f;
- } else if (diff < -180.0f) {
- newDir -= 360.f;
- }
- previousDirection = newDir;
-
- directionAnimator = ValueAnimator.ofFloat(oldDir, newDir);
- directionAnimator.setDuration(duration);
- directionAnimator.addUpdateListener(invalidateSelfOnUpdateListener);
- directionAnimator.start();
- }
-
- /**
- * Get the center of this view in screen coordinates.
- *
- * @return the center of the view
- */
- public PointF getCenter() {
- return new PointF(getCenterX(), getCenterY());
- }
-
- /**
- * Get the x value of the center of this view.
- *
- * @return the x value of the center of the view
- */
- private float getCenterX() {
- return (getX() + getMeasuredWidth()) / 2 + contentPaddingX - projectedX;
- }
-
- /**
- * Get the y value of the center of this view.
- *
- * @return the y value of the center of the view
- */
- private float getCenterY() {
- return (getY() + getMeasuredHeight()) / 2 + contentPaddingY - projectedY;
- }
-
- public void setContentPadding(int[] padding) {
- contentPaddingX = (padding[0] - padding[2]) / 2;
- contentPaddingY = (padding[1] - padding[3]) / 2;
- }
-
- /**
- * Set the location source from which location updates are received, for internal use only.
- *
- * @param locationEngine The location engine to receive updates from
- */
- public void setLocationSource(LocationEngine locationEngine) {
- toggleGps(false);
- this.locationEngine = locationEngine;
- this.userLocationListener = null;
- setEnabled(isEnabled(), locationEngine != null);
- }
-
- private void applyDrawableTint(Drawable drawable, @ColorInt int color) {
- if (color == UNDEFINED_TINT_COLOR) {
- removeTintColorFilter(drawable);
- } else {
- applyTintColorFilter(drawable, color);
- }
- }
-
- private void removeTintColorFilter(Drawable drawable) {
- if (drawable != null) {
- drawable.mutate().setColorFilter(null);
- }
- }
-
- private void applyTintColorFilter(Drawable drawable, @ColorInt int color) {
- if (drawable != null) {
- drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN);
- }
- }
-
- private void restoreLocationEngine() {
- locationEngine.setPriority(LocationEnginePriority.LOW_POWER);
- locationEngine.activate();
- }
-
- private static class GpsLocationListener implements LocationEngineListener {
-
- private WeakReference<MyLocationView> userLocationView;
- private WeakReference<LocationEngine> locationSource;
-
- GpsLocationListener(MyLocationView myLocationView, LocationEngine locationEngine) {
- userLocationView = new WeakReference<>(myLocationView);
- locationSource = new WeakReference<>(locationEngine);
- }
-
- @SuppressLint("MissingPermission")
- @Override
- public void onConnected() {
- MyLocationView locationView = userLocationView.get();
- LocationEngine locationEngine = locationSource.get();
- if (locationView != null && locationEngine != null) {
- Location lastKnownLocation = locationEngine.getLastLocation();
- if (lastKnownLocation != null) {
- locationView.setLocation(lastKnownLocation);
- }
- locationEngine.requestLocationUpdates();
- }
- }
-
- /**
- * Callback method for receiving location updates from LocationServices.
- *
- * @param location The new Location data
- */
- @Override
- public void onLocationChanged(Location location) {
- MyLocationView locationView = userLocationView.get();
- if (locationView != null) {
- locationView.setLocation(location);
- }
- }
- }
-
- private class CompassListener implements SensorEventListener {
-
- private final SensorManager sensorManager;
-
- private Sensor rotationVectorSensor;
- private float[] matrix = new float[9];
- private float[] rotationVectorValue;
- private float[] truncatedRotationVectorValue = new float[4];
-
- private float[] orientation = new float[3];
- private boolean reportMissingSensor = true;
- // Compass data
- private long compassUpdateNextTimestamp = 0;
-
- CompassListener(Context context) {
- sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
- }
-
- public void onResume() {
- sensorManager.registerListener(this, rotationVectorSensor, SensorManager.SENSOR_DELAY_GAME);
- }
-
- public void onPause() {
- sensorManager.unregisterListener(this, rotationVectorSensor);
- }
-
- public boolean isSensorAvailable() {
- if (rotationVectorSensor == null && reportMissingSensor) {
- reportMissingSensor = false;
- Timber.e("Sensor.TYPE_ROTATION_VECTOR is missing from this device. Unable to use MyBearingTracking.COMPASS.");
- }
- return rotationVectorSensor != null;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
-
- // check when the last time the compass was updated, return if too soon.
- long currentTime = SystemClock.elapsedRealtime();
- if (currentTime < compassUpdateNextTimestamp) {
- return;
- }
-
- if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) {
- rotationVectorValue = getRotationVectorFromSensorEvent(event);
- SensorManager.getRotationMatrixFromVector(matrix, rotationVectorValue);
- SensorManager.getOrientation(matrix, orientation);
-
- magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]);
- if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
- // Change the user location view orientation to reflect the device orientation
- rotateCamera(magneticHeading);
- setCompass(0, COMPASS_UPDATE_RATE_MS);
- } else {
- // Change compass direction
- setCompass(magneticHeading - bearing, COMPASS_UPDATE_RATE_MS);
- }
-
- compassUpdateNextTimestamp = currentTime + COMPASS_UPDATE_RATE_MS;
- }
- }
-
- /**
- * Pulls out the rotation vector from a SensorEvent, with a maximum length
- * vector of four elements to avoid potential compatibility issues.
- *
- * @param event the sensor event
- * @return the events rotation vector, potentially truncated
- */
- @NonNull
- float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) {
- if (event.values.length > 4) {
- // On some Samsung devices SensorManager.getRotationMatrixFromVector
- // appears to throw an exception if rotation vector has length > 4.
- // For the purposes of this class the first 4 values of the
- // rotation vector are sufficient (see crbug.com/335298 for details).
- // Only affects Android 4.3
- System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4);
- return truncatedRotationVectorValue;
- } else {
- return event.values;
- }
- }
-
- private void rotateCamera(float rotation) {
- CameraPosition.Builder builder = new CameraPosition.Builder();
- builder.bearing(rotation);
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS,
- false /*linear interpolator*/, null);
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- }
-
- private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener {
-
- private MyLocationBehavior behavior;
- private double fromLat;
- private double fromLng;
- private double toLat;
- private double toLng;
-
- private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) {
- behavior = myLocationBehavior;
- fromLat = from.getLatitude();
- fromLng = from.getLongitude();
- toLat = to.getLatitude();
- toLng = to.getLongitude();
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float frac = animation.getAnimatedFraction();
- double latitude = fromLat + (toLat - fromLat) * frac;
- double longitude = fromLng + (toLng - fromLng) * frac;
- behavior.updateLatLng(latitude, longitude);
- update();
- }
- }
-
- private class MyLocationBehaviorFactory {
-
- MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) {
- if (mode == MyLocationTracking.TRACKING_NONE) {
- return new MyLocationShowBehavior();
- } else {
- return new MyLocationTrackingBehavior();
- }
- }
- }
-
- private abstract class MyLocationBehavior {
-
- MyLocationBehavior() {
- if (latLng != null) {
- locationUpdateTimestamp = SystemClock.elapsedRealtime();
- }
- }
-
- void updateLatLng(@NonNull Location newLocation) {
- location = newLocation;
- }
-
- void updateLatLng(double lat, double lon) {
- if (latLng != null) {
- latLng.setLatitude(lat);
- latLng.setLongitude(lon);
- }
- }
-
- void updateAccuracy(@NonNull Location location) {
- if (accuracyAnimator != null && accuracyAnimator.isRunning()) {
- // use current accuracy as a starting point
- accuracy = (Float) accuracyAnimator.getAnimatedValue();
- accuracyAnimator.end();
- }
-
- float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f;
- accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy);
- accuracyAnimator.setDuration(750);
- accuracyAnimator.start();
- accuracy = newAccuracy;
- }
-
- abstract void invalidate();
- }
-
- private class MyLocationTrackingBehavior extends MyLocationBehavior {
-
- @Override
- void updateLatLng(@NonNull Location location) {
- super.updateLatLng(location);
- if (latLng == null) {
- // first location fix
- latLng = new LatLng(location);
- locationUpdateTimestamp = SystemClock.elapsedRealtime();
- }
-
- // updateLatLng timestamp
- float previousUpdateTimeStamp = locationUpdateTimestamp;
- locationUpdateTimestamp = SystemClock.elapsedRealtime();
-
- // calculate animation duration
- int animationDuration;
- if (previousUpdateTimeStamp == 0) {
- animationDuration = 0;
- } else {
- animationDuration = (int) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f)
- /*make animation slightly longer*/;
- }
-
- // calculate interpolated location
- latLng = new LatLng(location);
- CameraPosition.Builder builder = new CameraPosition.Builder().target(latLng);
-
- // add direction
- if (myBearingTrackingMode == MyBearingTracking.GPS) {
- if (location.hasBearing()) {
- builder.bearing(location.getBearing());
- }
- setCompass(0, COMPASS_UPDATE_RATE_MS);
- }
-
- if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) {
- builder.bearing(0);
- if (location.hasBearing()) {
- setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS);
- }
- }
-
- // accuracy
- updateAccuracy(location);
-
- if (locationChangeAnimationEnabled && animationDuration > 0) {
- // ease to new camera position with a linear interpolator
- mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null,
- true);
- } else {
- mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build()));
- }
- }
-
- @Override
- void invalidate() {
- int[] mapPadding = mapboxMap.getPadding();
- float x = (getWidth() + mapPadding[0] - mapPadding[2]) / 2 + contentPaddingX;
- float y = (getHeight() - mapPadding[3] + mapPadding[1]) / 2 + contentPaddingY;
- screenLocation = new PointF(x, y);
- MyLocationView.this.invalidate();
- }
- }
-
- private class MyLocationShowBehavior extends MyLocationBehavior {
-
- @Override
- void updateLatLng(@NonNull final Location location) {
- super.updateLatLng(location);
- if (latLng == null) {
- // first location update
- latLng = new LatLng(location);
- locationUpdateTimestamp = SystemClock.elapsedRealtime();
- }
-
- // update LatLng location
- LatLng newLocation = new LatLng(location);
-
- // update LatLng accuracy
- updateAccuracy(location);
-
- // calculate updateLatLng time + add some extra offset to improve animation
- long previousUpdateTimeStamp = locationUpdateTimestamp;
- locationUpdateTimestamp = SystemClock.elapsedRealtime();
- long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.2f);
-
- // animate changes
- if (locationChangeAnimator != null) {
- locationChangeAnimator.end();
- locationChangeAnimator = null;
- }
-
- locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- if (locationChangeAnimationEnabled) {
- locationChangeAnimator.setDuration(locationUpdateDuration);
- } else {
- locationChangeAnimator.setDuration(0);
- }
- locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this,
- latLng, newLocation
- ));
- locationChangeAnimator.start();
- latLng = newLocation;
- }
-
- @Override
- void invalidate() {
- if (latLng != null) {
- screenLocation = projection.toScreenLocation(latLng);
- }
- MyLocationView.this.invalidate();
- }
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
deleted file mode 100644
index ec7c53e1d0..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java
+++ /dev/null
@@ -1,389 +0,0 @@
-package com.mapbox.mapboxsdk.maps.widgets;
-
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.support.annotation.ColorInt;
-import android.support.annotation.IntRange;
-import android.support.annotation.NonNull;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.constants.MapboxConstants;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.maps.FocalPointChangeListener;
-import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.mapboxsdk.utils.BitmapUtils;
-
-/**
- * Settings to configure the visual appearance of the MyLocationView.
- *
- * @deprecated use location layer plugin from
- * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead.
- */
-@Deprecated
-public class MyLocationViewSettings {
-
- private Projection projection;
- private MyLocationView myLocationView;
- private FocalPointChangeListener focalPointChangeListener;
-
- //
- // State
- //
-
- private boolean enabled;
-
- //
- // Foreground
- //
-
- private Drawable foregroundDrawable;
- private Drawable foregroundBearingDrawable;
-
- @ColorInt
- private int foregroundTintColor;
-
- //
- // Background
- //
-
- private Drawable backgroundDrawable;
- private int[] backgroundOffset = new int[4];
-
- @ColorInt
- private int backgroundTintColor;
-
- //
- // Accuracy
- //
-
- private int accuracyAlpha;
- private float accuracyThreshold = 0f;
-
- @ColorInt
- private int accuracyTintColor;
-
- //
- // Padding
- //
-
- private int[] padding = new int[4];
-
- /**
- * Creates an instance of MyLocationViewSettings
- * <p>
- *
- * @param myLocationView the MyLocationView to apply the settings to
- * @param projection the MapView projection
- * @param focalPointChangedListener the interface to be invoked when focal points changes
- * @see MyLocationView
- */
- public MyLocationViewSettings(MyLocationView myLocationView, Projection projection, FocalPointChangeListener
- focalPointChangedListener) {
- this.myLocationView = myLocationView;
- this.projection = projection;
- this.focalPointChangeListener = focalPointChangedListener;
- }
-
- /**
- * Initialise this with MapboxMapOptions.
- *
- * @param options the options to initialise this class from
- */
- public void initialise(@NonNull MapboxMapOptions options) {
- CameraPosition position = options.getCamera();
- if (position != null && !position.equals(CameraPosition.DEFAULT)) {
- setTilt(position.tilt);
- }
- setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable());
- setForegroundTintColor(options.getMyLocationForegroundTintColor());
- setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding());
- setBackgroundTintColor(options.getMyLocationBackgroundTintColor());
- setAccuracyAlpha(options.getMyLocationAccuracyAlpha());
- setAccuracyTintColor(options.getMyLocationAccuracyTintColor());
- setAccuracyThreshold(options.getMyLocationAccuracyThreshold());
- }
-
- public void onSaveInstanceState(Bundle outState) {
- outState.putBoolean(MapboxConstants.STATE_LOCATION_VIEW_ENABLED, isEnabled());
- outState.putByteArray(
- MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_DRAWABLE,
- BitmapUtils.getByteArrayFromDrawable(getForegroundDrawable())
- );
- outState.putByteArray(
- MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_BEARING_DRAWABLE,
- BitmapUtils.getByteArrayFromDrawable(getForegroundBearingDrawable())
- );
- outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_TINT_COLOR, getForegroundTintColor());
- outState.putByteArray(
- MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_DRAWABLE,
- BitmapUtils.getByteArrayFromDrawable(getBackgroundDrawable())
- );
- outState.putIntArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_OFFSET, getBackgroundOffset());
- outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_TINT_COLOR, getBackgroundTintColor());
- outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_ALPHA, getAccuracyAlpha());
- outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_TINT_COLOR, getAccuracyTintColor());
- outState.putFloat(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_THRESHOLD, getAccuracyThreshold());
- outState.putIntArray(MapboxConstants.STATE_LOCATION_VIEW_PADDING, getPadding());
- }
-
- public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
- setEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_LOCATION_VIEW_ENABLED));
- setForegroundDrawable(
- BitmapUtils.getDrawableFromByteArray(
- myLocationView.getContext(),
- savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_DRAWABLE)
- ),
- BitmapUtils.getDrawableFromByteArray(
- myLocationView.getContext(),
- savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_BEARING_DRAWABLE)
- )
- );
- setForegroundTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_TINT_COLOR));
- setBackgroundDrawable(
- BitmapUtils.getDrawableFromByteArray(
- myLocationView.getContext(),
- savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_DRAWABLE)
- ),
- savedInstanceState.getIntArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_OFFSET)
- );
- setBackgroundTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_TINT_COLOR));
- setAccuracyAlpha(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_ALPHA));
- setAccuracyTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_TINT_COLOR));
- setAccuracyThreshold(savedInstanceState.getFloat(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_THRESHOLD));
- setPadding(savedInstanceState.getIntArray(MapboxConstants.STATE_LOCATION_VIEW_PADDING));
- }
-
- /**
- * Returns if the MyLocationView is enabled
- *
- * @return true if MyLocationView is enabled,
- */
- public boolean isEnabled() {
- return enabled;
- }
-
- /**
- * Set the enabled state of MyLocationView
- *
- * @param enabled true shows the MyLocationView on the map
- */
- public void setEnabled(boolean enabled) {
- this.enabled = enabled;
- myLocationView.setEnabled(enabled);
- }
-
- /**
- * Set the foreground drawable of the MyLocationView
- * <p>
- * The foreground drawable is the image visible on screen
- * </p>
- * It's linked with the foreground tint color
- *
- * @param foregroundDrawable the drawable to show as foreground without bearing
- * @param foregroundBearingDrawable the drawable to show as foreground when bearing is enabled
- */
- public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) {
- this.foregroundDrawable = foregroundDrawable;
- this.foregroundBearingDrawable = foregroundBearingDrawable;
- myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
- myLocationView.setForegroundDrawableTint(foregroundTintColor);
- }
-
- /**
- * Get the foreground drawable when bearing is disabled.
- *
- * @return the drawable used as foreground
- */
- public Drawable getForegroundDrawable() {
- return foregroundDrawable;
- }
-
- /**
- * Get the foreground drawable when bearing is enabled.
- *
- * @return the bearing drawable used as foreground
- */
- public Drawable getForegroundBearingDrawable() {
- return foregroundBearingDrawable;
- }
-
- /**
- * Set the foreground tint color.
- * <p>
- * The color will tint both the foreground and the bearing foreground drawable.
- * </p>
- *
- * @param foregroundTintColor the color to tint the foreground drawable or -1 (undefined color) to remove the
- * existing foreground tint color
- */
- public void setForegroundTintColor(@ColorInt int foregroundTintColor) {
- this.foregroundTintColor = foregroundTintColor;
- myLocationView.setForegroundDrawableTint(foregroundTintColor);
- }
-
- /**
- * Get the foreground tint color.
- *
- * @return the foreground tint color
- */
- public int getForegroundTintColor() {
- return foregroundTintColor;
- }
-
- /**
- * Set the background drawable of MyLocationView
- * <p>
- * Padding can be added to provide an offset to the background
- * </p>
- * It's linked with the background tint color
- *
- * @param backgroundDrawable the drawable to show as background
- * @param padding the padding added to the background
- */
- public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) {
- this.backgroundDrawable = backgroundDrawable;
- this.backgroundOffset = padding;
- if (padding != null && padding.length == 4) {
- myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]);
- } else {
- myLocationView.setShadowDrawable(backgroundDrawable);
- }
- myLocationView.setShadowDrawableTint(backgroundTintColor);
- }
-
- /**
- * Get the background drawable of MyLocationView.
- *
- * @return the drawable used as background
- */
- public Drawable getBackgroundDrawable() {
- return backgroundDrawable;
- }
-
- /**
- * Set the background tint color.
- *
- * @param backgroundTintColor the color to tint the background drawable or -1 (undefined color) to remove the
- * existing background tint color
- */
- public void setBackgroundTintColor(@ColorInt int backgroundTintColor) {
- this.backgroundTintColor = backgroundTintColor;
- myLocationView.setShadowDrawableTint(backgroundTintColor);
- }
-
- /**
- * Get the background tint color.
- *
- * @return the background tint color
- */
- public int getBackgroundTintColor() {
- return backgroundTintColor;
- }
-
- /**
- * Get the background offset.
- *
- * @return the background offset
- */
- public int[] getBackgroundOffset() {
- return backgroundOffset;
- }
-
- /**
- * Set the MyLocationView padding.
- *
- * @param left the padding left of MyLocationView
- * @param top the padding top of MyLocationView
- * @param right the padding right of MyLocationView
- * @param bottom the padding bottom of MyLocaionView
- */
- public void setPadding(int left, int top, int right, int bottom) {
- padding = new int[] {left, top, right, bottom};
- setPadding(padding);
- }
-
- private void setPadding(int[] padding) {
- myLocationView.setContentPadding(padding);
- projection.invalidateContentPadding(padding);
- invalidateFocalPointForTracking(myLocationView);
- }
-
- /**
- * Get the MyLocationView padding.
- *
- * @return an array describing the padding in a LTRB manner
- */
- public int[] getPadding() {
- return padding;
- }
-
- /**
- * Get the alpha value of the accuracy circle of MyLocationView
- *
- * @return the alpha value
- */
- public int getAccuracyAlpha() {
- return accuracyAlpha;
- }
-
- /**
- * Set the alpha value of the accuracy circle of MyLocationView
- *
- * @param accuracyAlpha the alpha value to set
- */
- public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int accuracyAlpha) {
- this.accuracyAlpha = accuracyAlpha;
- myLocationView.setAccuracyAlpha(accuracyAlpha);
- }
-
- /**
- * Get the accuracy tint color of MyLocationView.
- *
- * @return the tint color used for accuracy
- */
- public int getAccuracyTintColor() {
- return accuracyTintColor;
- }
-
- /**
- * Set the accuracy tint color of MyLocationView.
- *
- * @param accuracyTintColor the accuracy tint color
- */
- public void setAccuracyTintColor(@ColorInt int accuracyTintColor) {
- this.accuracyTintColor = accuracyTintColor;
- myLocationView.setAccuracyTint(accuracyTintColor);
- }
-
- /**
- * Returns current accuracy threshold value (in meters).
- *
- * @return Value of accuracy threshold (in meters), below which circle won't be displayed
- */
- public float getAccuracyThreshold() {
- return accuracyThreshold;
- }
-
- /**
- * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value.
- *
- * @param accuracyThreshold Value of accuracy (in meters), below which circle won't be displayed
- */
- public void setAccuracyThreshold(float accuracyThreshold) {
- this.accuracyThreshold = accuracyThreshold;
- myLocationView.setAccuracyThreshold(accuracyThreshold);
- }
-
- public void setTilt(double tilt) {
- myLocationView.setTilt(tilt);
- }
-
- private void invalidateFocalPointForTracking(MyLocationView myLocationView) {
- if (!(myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE)) {
- focalPointChangeListener.onFocalPointChanged(myLocationView.getCenter());
- } else {
- focalPointChangeListener.onFocalPointChanged(null);
- }
- }
-}
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..946d3694d5 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
@@ -5,7 +5,6 @@ import android.os.Looper;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-
import com.mapbox.mapboxsdk.LibraryLoader;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.storage.FileSource;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
index 2a32f0bdd6..ea9a066df7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java
@@ -10,12 +10,12 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
* An offline region defined by a style URL, geographic bounding box, zoom range, and
* device pixel ratio.
* <p>
- * Both minZoom and maxZoom must be ≥ 0, and maxZoom must be ≥ minZoom.
+ * Both minZoom and maxZoom must be &#x2265; 0, and maxZoom must be &#x2265; minZoom.
* <p>
- * maxZoom may be ∞, in which case for each tile source, the region will include
+ * maxZoom may be &#x221E;, in which case for each tile source, the region will include
* tiles from minZoom up to the maximum zoom level provided by that source.
* <p>
- * pixelRatio must be ≥ 0 and should typically be 1.0 or 2.0.
+ * pixelRatio must be &#x2265; 0 and should typically be 1.0 or 2.0.
*/
public class OfflineTilePyramidRegionDefinition implements OfflineRegionDefinition, Parcelable {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java
new file mode 100644
index 0000000000..f1ce247ba8
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android Snapshotter API classes.
+ */
+package com.mapbox.mapboxsdk.snapshotter;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
index f0cb8d973a..929e4b4279 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java
@@ -6,6 +6,8 @@ import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.os.Environment;
import android.support.annotation.NonNull;
+import android.support.annotation.UiThread;
+
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import timber.log.Timber;
@@ -43,6 +45,7 @@ public class FileSource {
* @param context the context to derive the cache path from
* @return the single instance of FileSource
*/
+ @UiThread
public static synchronized FileSource getInstance(Context context) {
if (INSTANCE == null) {
String cachePath = getCachePath(context);
@@ -122,6 +125,8 @@ public class FileSource {
initialize(Mapbox.getAccessToken(), cachePath, assetManager);
}
+ public native boolean isActivated();
+
public native void activate();
public native void deactivate();
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..bd5b40c6ce
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java
@@ -0,0 +1,3762 @@
+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.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
+import com.mapbox.mapboxsdk.style.layers.PropertyValue;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+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:
+ * <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>
+ * 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>
+ * <p>
+ * Example expression:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * 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))
+ * ))
+ * )
+ * )
+ * )
+ * }
+ * </pre>
+ */
+public class Expression {
+
+ 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
+ */
+ public Expression(@NonNull String operator, @Nullable Expression... arguments) {
+ this.operator = operator;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Create a literal number expression.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(literal(10.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number
+ * @return the expression
+ */
+ public static Expression literal(@NonNull Number number) {
+ return new ExpressionLiteral(number);
+ }
+
+ /**
+ * Create a literal string expression.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(literal("Text"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param string the string
+ * @return the expression
+ */
+ public static Expression literal(@NonNull String string) {
+ return new ExpressionLiteral(string);
+ }
+
+ /**
+ * Create a literal boolean expression.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillAntialias(literal(true))
+ * );
+ * }
+ * </pre>
+ *
+ * @param bool the boolean
+ * @return the expression
+ */
+ public static Expression literal(boolean bool) {
+ return new ExpressionLiteral(bool);
+ }
+
+ /**
+ * Create a literal object expression.
+ *
+ * @param object the object
+ * @return the expression
+ */
+ public static Expression literal(@NonNull Object object) {
+ if (object.getClass().isArray()) {
+ return literal(ExpressionArray.toObjectArray(object));
+ }
+ return new ExpressionLiteral(object);
+ }
+
+ /**
+ * Create a literal array expression
+ *
+ * @param array the array
+ * @return the expression
+ */
+ public static Expression literal(@NonNull Object[] array) {
+ return new ExpressionArray(array);
+ }
+
+ /**
+ * Expression literal utility method to convert a color int to an color expression
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(color(Color.GREEN))
+ * );
+ * }
+ * </pre>
+ *
+ * @param color the int color
+ * @return the color expression
+ */
+ public static Expression color(@ColorInt int color) {
+ return toColor(literal(PropertyFactory.colorToRgbaString(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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * rgb(
+ * literal(255.0f),
+ * literal(255.0f),
+ * literal(255.0f)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param red red color expression
+ * @param green green color expression
+ * @param blue blue color expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-rgb">Style specification</a>
+ */
+ public static Expression rgb(@NonNull Expression red, @NonNull Expression green, @NonNull Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * rgb(255.0f, 255.0f, 255.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-rgb">Style specification</a>
+ */
+ public static Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * rgba(
+ * literal(255.0f),
+ * literal(255.0f),
+ * literal(255.0f),
+ * literal(1.0f)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-rgba">Style specification</a>
+ */
+ public static Expression rgba(@NonNull Expression red, @NonNull Expression green,
+ @NonNull Expression blue, @NonNull Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * rgb(255.0f, 255.0f, 255.0f, 1.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param red red color value
+ * @param green green color value
+ * @param blue blue color value
+ * @param alpha alpha color value
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-rgba">Style specification</a>
+ */
+ public static Expression 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-to-rgba">Style specification</a>
+ */
+ public static Expression toRgba(@NonNull Expression expression) {
+ return new Expression("to-rgba", expression);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * The inputs must be numbers, strings, or booleans, and both of the same type.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * eq(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a>
+ */
+ public static Expression eq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression("==", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * eq(get("keyToValue"), true)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second boolean
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a>
+ */
+ public static Expression eq(Expression compareOne, boolean compareTwo) {
+ return eq(compareOne, literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * eq(get("keyToValue"), "value")
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a>
+ */
+ public static Expression eq(@NonNull Expression compareOne, @NonNull String compareTwo) {
+ return eq(literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * eq(get("keyToValue"), 2.0f)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-==">Style specification</a>
+ */
+ public static Expression eq(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * neq(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a>
+ */
+ public static Expression neq(@NonNull Expression compareOne, @NonNull Expression compareTwo) {
+ return new Expression("!=", compareOne, compareTwo);
+ }
+
+ /**
+ * Returns true if the input values are equal, false otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * neq(get("keyToValue"), true)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second boolean
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a>
+ */
+ public static Expression neq(Expression compareOne, boolean compareTwo) {
+ return new Expression("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * neq(get("keyToValue"), "value"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a>
+ */
+ public static Expression neq(@NonNull Expression compareOne, @NonNull String compareTwo) {
+ return new Expression("!=", literal(compareOne), literal(compareTwo));
+ }
+
+ /**
+ * Returns `true` if the input values are not equal, `false` otherwise.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * neq(get("keyToValue"), 2.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!=">Style specification</a>
+ */
+ public static Expression neq(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * gt(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * gt(get("keyToValue"), 2.0f)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E">Style specification</a>
+ */
+ public static Expression gt(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * gt(get("keyToValue"), "value")
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E">Style specification</a>
+ */
+ public static Expression gt(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lt(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lt(get("keyToValue"), 2.0f)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a>
+ */
+ public static Expression lt(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lt(get("keyToValue"), "value"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C">Style specification</a>
+ */
+ public static Expression lt(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * gte(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E%3D">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * gte(get("keyToValue"), 2.0f)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E%3D">Style specification</a>
+ */
+ public static Expression gte(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * neq(get("keyToValue"), "value")
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3E%3D">Style specification</a>
+ */
+ public static Expression gte(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lte(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C%3D">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lte(get("keyToValue"), 2.0f)
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C%3D">Style specification</a>
+ */
+ public static Expression lte(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * lte(get("keyToValue"), "value")
+ * );
+ * }
+ * </pre>
+ *
+ * @param compareOne the first expression
+ * @param compareTwo the second string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%3C%3D">Style specification</a>
+ */
+ public static Expression lte(@NonNull Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * all(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-all">Style specification</a>
+ */
+ public static Expression all(@NonNull Expression... 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * any(get("keyToValue"), get("keyToOtherValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-any">Style specification</a>
+ */
+ public static Expression any(@NonNull Expression... input) {
+ return new Expression("any", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * not(get("keyToValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!">Style specification</a>
+ */
+ public static Expression not(@NonNull Expression input) {
+ return new Expression("!", input);
+ }
+
+ /**
+ * Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * not(false)
+ * );
+ * }
+ * </pre>
+ *
+ * @param input boolean input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-!">Style specification</a>
+ */
+ public static Expression not(boolean input) {
+ return not(literal(input));
+ }
+
+ /**
+ * Selects the first output whose corresponding test condition evaluates to true.
+ * <p>
+ * For each case a condition and an output should be provided.
+ * The last parameter should provide the default output.
+ * </p>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * iconSize(
+ * switchCase(
+ * get(KEY_TO_BOOLEAN), literal(3.0f),
+ * get(KEY_TO_OTHER_BOOLEAN), literal(5.0f)
+ * literal(1.0f) // default value
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-case">Style specification</a>
+ */
+ 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.
+ * If types of the input and keys don't match, or the input value doesn't exist,
+ * the expresion will fail without falling back to the default value.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textColor(
+ * match(get("keyToValue"),
+ * literal(1), rgba(255, 0, 0, 1.0f),
+ * literal(2), rgba(0, 0, 255.0f, 1.0f),
+ * rgba(0.0f, 255.0f, 0.0f, 1.0f)
+ * );
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-match">Style specification</a>
+ */
+ 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.
+ * If types of the input and keys don't match, or the input value doesn't exist,
+ * the expresion will fail without falling back to the default value.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textColor(
+ * match(get("keyToValue"),
+ * literal(1), rgba(255, 0, 0, 1.0f),
+ * literal(2), rgba(0, 0, 255.0f, 1.0f),
+ * rgba(0.0f, 255.0f, 0.0f, 1.0f)
+ * );
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-match">Style specification</a>
+ */
+ public static Expression match(@NonNull Expression input, @NonNull Expression defaultOutput, @NonNull Stop... stops) {
+ Expression[] expressionStops = new Expression[stops.length * 2];
+ for (int i = 0; i < stops.length; i++) {
+ expressionStops[i * 2] = literal(stops[i].value);
+ expressionStops[i * 2 + 1] = literal(stops[i].output);
+ }
+ return match(join(join(new Expression[] {input}, expressionStops), new Expression[] {defaultOutput}));
+ }
+
+ /**
+ * Evaluates each expression in turn until the first non-null value is obtained, and returns that value.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textColor(
+ * coalesce(
+ * get("keyToNullValue"),
+ * get("keyToNonNullValue")
+ * );
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-coalesce">Style specification</a>
+ */
+ public static Expression coalesce(@NonNull Expression... input) {
+ return new Expression("coalesce", input);
+ }
+
+ /**
+ * Gets the feature properties object.
+ * <p>
+ * Note that in some cases, it may be more efficient to use {@link #get(Expression)}} instead.
+ * </p>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(get("key-to-value", properties())))
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-properties">Style specification</a>
+ */
+ public static Expression properties() {
+ return new Expression("properties");
+ }
+
+ /**
+ * Gets the feature's geometry type: Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(concat(get("key-to-value"), literal(" "), geometryType())
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-geometry-types">Style specification</a>
+ */
+ public static Expression geometryType() {
+ return new Expression("geometry-type");
+ }
+
+ /**
+ * Gets the feature's id, if it has one.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(id())
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-id">Style specification</a>
+ */
+ public static Expression id() {
+ return new Expression("id");
+ }
+
+ /**
+ * 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * HeatmapLayer layer = new HeatmapLayer("layer-id", "source-id");
+ * layer.setProperties(
+ * heatmapColor(interpolate(linear(), heatmapDensity(),
+ * literal(0), rgba(33, 102, 172, 0),
+ * literal(0.2), rgb(103, 169, 207),
+ * literal(0.4), rgb(209, 229, 240),
+ * literal(0.6), rgb(253, 219, 199),
+ * literal(0.8), rgb(239, 138, 98),
+ * literal(1), rgb(178, 24, 43)
+ * )
+ * )
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-heatmap-density">Style specification</a>
+ */
+ public static Expression heatmapDensity() {
+ return new Expression("heatmap-density");
+ }
+
+ /**
+ * Retrieves an item from an array.
+ *
+ * @param number the index expression
+ * @param expression the array expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-at">Style specification</a>
+ */
+ public static Expression at(@NonNull Expression 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-at">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(get("key-to-feature"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-get">Style specification</a>
+ */
+ public static Expression get(@NonNull Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(get("key-to-feature"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input string input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-get">Style specification</a>
+ */
+ 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(get("key-to-property", get("key-to-object")))
+ * );
+ * }
+ * </pre>
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-get">Style specification</a>
+ */
+ public static Expression get(@NonNull Expression key, @NonNull Expression object) {
+ return new Expression("get", key, object);
+ }
+
+ /**
+ * Retrieves a property value from another object.
+ * Returns null if the requested property is missing.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(get("key-to-property", get("key-to-object")))
+ * );
+ * }
+ * </pre>
+ *
+ * @param key a property value key
+ * @param object an expression object
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-get">Style specification</a>
+ */
+ public static Expression get(@NonNull String key, @NonNull Expression object) {
+ return get(literal(key), object);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * has(get("keyToValue"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param key the expression property value key
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has">Style specification</a>
+ */
+ public static Expression has(@NonNull Expression key) {
+ return new Expression("has", key);
+ }
+
+ /**
+ * Tests for the presence of an property value in the current feature's properties.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * has("keyToValue")
+ * );
+ * }
+ * </pre>
+ *
+ * @param key the property value key
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has">Style specification</a>
+ */
+ public static Expression has(@NonNull String key) {
+ return has(literal(key));
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * has(get("keyToValue"), get("keyToObject"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param key the expression property value key
+ * @param object an expression object
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has">Style specification</a>
+ */
+ public static Expression has(@NonNull Expression key, @NonNull Expression object) {
+ return new Expression("has", key, object);
+ }
+
+ /**
+ * Tests for the presence of an property value from another object.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setFilter(
+ * has("keyToValue", get("keyToObject))
+ * );
+ * }
+ * </pre>
+ *
+ * @param key the property value key
+ * @param object an expression object
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has">Style specification</a>
+ */
+ public static Expression has(@NonNull String key, @NonNull Expression object) {
+ return has(literal(key), object);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param expression an expression object or expression string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-lenght">Style specification</a>
+ */
+ public static Expression length(@NonNull Expression expression) {
+ return new Expression("length", expression);
+ }
+
+ /**
+ * Gets the length of an array or string.
+ *
+ * @param input a string
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-lenght">Style specification</a>
+ */
+ public static Expression length(@NonNull String input) {
+ return length(literal(input));
+ }
+
+ /**
+ * Returns mathematical constant ln(2).
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(product(literal(10.0f), ln2())))
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-ln2">Style specification</a>
+ */
+ public static Expression ln2() {
+ return new Expression("ln2");
+ }
+
+ /**
+ * Returns the mathematical constant pi.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(product(literal(10.0f), pi())))
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-pi">Style specification</a>
+ */
+ public static Expression pi() {
+ return new Expression("pi");
+ }
+
+ /**
+ * Returns the mathematical constant e.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(product(literal(10.0f), e())))
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-e">Style specification</a>
+ */
+ public static Expression e() {
+ return new Expression("e");
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sum(literal(10.0f), ln2(), pi())))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-+">Style specification</a>
+ */
+ public static Expression sum(@Size(min = 2) Expression... numbers) {
+ return new Expression("+", numbers);
+ }
+
+ /**
+ * Returns the sum of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sum(10.0f, 5.0f, 3.0f)))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers the numbers to calculate the sum for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-+">Style specification</a>
+ */
+ public static Expression sum(@Size(min = 2) Number... numbers) {
+ Expression[] numberExpression = 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(product(literal(10.0f), ln2())))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-*">Style specification</a>
+ */
+ public static Expression product(@Size(min = 2) Expression... numbers) {
+ return new Expression("*", numbers);
+ }
+
+ /**
+ * Returns the product of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(product(10.0f, 2.0f)))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers the numbers to calculate the product for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-*">Style specification</a>
+ */
+ public static Expression product(@Size(min = 2) Number... numbers) {
+ Expression[] numberExpression = 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(subtract(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions--">Style specification</a>
+ */
+ public static Expression subtract(@NonNull Expression number) {
+ return new Expression("-", number);
+ }
+
+ /**
+ * Returns the result of subtracting a number from 0.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(subtract(10.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number subtract from 0
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions--">Style specification</a>
+ */
+ public static Expression subtract(@NonNull Number number) {
+ return subtract(literal(number));
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(subtract(literal(10.0f), pi())))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions--">Style specification</a>
+ */
+ public static Expression subtract(@NonNull Expression first, @NonNull Expression second) {
+ return new Expression("-", first, second);
+ }
+
+ /**
+ * Returns the result of subtracting the second input from the first.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(subtract(10.0f, 20.0f)))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions--">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(division(literal(10.0f), pi())))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-/">Style specification</a>
+ */
+ public static Expression division(@NonNull Expression first, @NonNull Expression second) {
+ return new Expression("/", first, second);
+ }
+
+ /**
+ * Returns the result of floating point division of the first input by the second.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(division(10.0f, 20.0f)))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-/">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(mod(literal(10.0f), pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%25">Style specification</a>
+ */
+ public static Expression mod(@NonNull Expression first, @NonNull Expression second) {
+ return new Expression("%", first, second);
+ }
+
+ /**
+ * Returns the remainder after integer division of the first input by the second.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(mod(10.0f, 10.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%25">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(pow(pi(), literal(2.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%5E">Style specification</a>
+ */
+ public static Expression pow(@NonNull Expression first, @NonNull Expression second) {
+ return new Expression("^", first, second);
+ }
+
+ /**
+ * Returns the result of raising the first input to the power specified by the second.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(pow(5.0f, 2.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param first the first number
+ * @param second the second number
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-%5E">Style specification</a>
+ */
+ public static Expression pow(@NonNull Number first, @NonNull Number second) {
+ return pow(literal(first), literal(second));
+ }
+
+ /**
+ * Returns the square root of the input
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sqrt(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sqrt">Style specification</a>
+ */
+ public static Expression sqrt(@NonNull Expression number) {
+ return new Expression("sqrt", number);
+ }
+
+ /**
+ * Returns the square root of the input
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sqrt(25.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take the square root from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sqrt">Style specification</a>
+ */
+ public static Expression sqrt(@NonNull Number number) {
+ return sqrt(literal(number));
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(log10(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log10">Style specification</a>
+ */
+ public static Expression log10(@NonNull Expression number) {
+ return new Expression("log10", number);
+ }
+
+ /**
+ * Returns the base-ten logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(log10(10))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take base-ten logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log10">Style specification</a>
+ */
+ public static Expression log10(@NonNull Number number) {
+ return log10(literal(number));
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(ln(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-ln">Style specification</a>
+ */
+ public static Expression ln(Expression number) {
+ return new Expression("ln", number);
+ }
+
+ /**
+ * Returns the natural logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(ln(10))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take natural logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-ln">Style specification</a>
+ */
+ public static Expression ln(Number number) {
+ return ln(literal(number));
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(log2(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log2">Style specification</a>
+ */
+ public static Expression log2(@NonNull Expression number) {
+ return new Expression("log2", number);
+ }
+
+ /**
+ * Returns the base-two logarithm of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(log2(2))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to take base-two logarithm from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log2">Style specification</a>
+ */
+ public static Expression log2(@NonNull Number number) {
+ return log2(literal(number));
+ }
+
+ /**
+ * Returns the sine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sin(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sin">Style specification</a>
+ */
+ public static Expression sin(@NonNull Expression number) {
+ return new Expression("sin", number);
+ }
+
+ /**
+ * Returns the sine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(sin(90.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the sine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sin">Style specification</a>
+ */
+ public static Expression sin(@NonNull Number number) {
+ return sin(literal(number));
+ }
+
+ /**
+ * Returns the cosine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(cos(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-cos">Style specification</a>
+ */
+ public static Expression cos(@NonNull Expression number) {
+ return new Expression("cos", number);
+ }
+
+ /**
+ * Returns the cosine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(cos(0)))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the cosine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-cos">Style specification</a>
+ */
+ public static Expression cos(@NonNull Number number) {
+ return new Expression("cos", literal(number));
+ }
+
+ /**
+ * Returns the tangent of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(tan(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-tan">Style specification</a>
+ */
+ public static Expression tan(@NonNull Expression number) {
+ return new Expression("tan", number);
+ }
+
+ /**
+ * Returns the tangent of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(tan(45.0f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the tangent for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-tan">Style specification</a>
+ */
+ public static Expression tan(@NonNull Number number) {
+ return new Expression("tan", literal(number));
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(asin(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-asin">Style specification</a>
+ */
+ public static Expression asin(@NonNull Expression number) {
+ return new Expression("asin", number);
+ }
+
+ /**
+ * Returns the arcsine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(asin(90))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arcsine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-asin">Style specification</a>
+ */
+ public static Expression asin(@NonNull Number number) {
+ return asin(literal(number));
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(acos(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-acos">Style specification</a>
+ */
+ public static Expression acos(@NonNull Expression number) {
+ return new Expression("acos", number);
+ }
+
+ /**
+ * Returns the arccosine of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(acos(0))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arccosine for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-acos">Style specification</a>
+ */
+ public static Expression acos(@NonNull Number number) {
+ return acos(literal(number));
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(asin(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-atan">Style specification</a>
+ */
+ public static Expression atan(@NonNull Expression number) {
+ return new Expression("atan", number);
+ }
+
+ /**
+ * Returns the arctangent of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(atan(90))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number the number to calculate the arctangent for
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-atan">Style specification</a>
+ */
+ public static Expression atan(@NonNull Number number) {
+ return atan(literal(number));
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(min(pi(), literal(3.14f), literal(3.15f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-min">Style specification</a>
+ */
+ public static Expression min(@Size(min = 1) Expression... numbers) {
+ return new Expression("min", numbers);
+ }
+
+ /**
+ * Returns the minimum value of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(min(3.141, 3.14f, 3.15f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers varargs of numbers to get the minimum from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-min">Style specification</a>
+ */
+ public static Expression min(@Size(min = 1) Number... numbers) {
+ Expression[] numberExpression = 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(max(pi(), 3.14f, 3.15f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-max">Style specification</a>
+ */
+ public static Expression max(@Size(min = 1) Expression... numbers) {
+ return new Expression("max", numbers);
+ }
+
+ /**
+ * Returns the maximum value of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(max(3.141, 3.14f, 3.15f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param numbers varargs of numbers to get the maximum from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-max">Style specification</a>
+ */
+ public static Expression max(@Size(min = 1) Number... numbers) {
+ Expression[] numberExpression = new Expression[numbers.length];
+ for (int i = 0; i < numbers.length; i++) {
+ numberExpression[i] = literal(numbers[i]);
+ }
+ return max(numberExpression);
+ }
+
+ /**
+ * Rounds the input to the nearest integer.
+ * Halfway values are rounded away from zero.
+ * For example `[\"round\", -1.5]` evaluates to -2.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(round(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param expression number expression to round
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-round">Style specification</a>
+ */
+ public static Expression round(Expression expression) {
+ return new Expression("round", expression);
+ }
+
+ /**
+ * Rounds the input to the nearest integer.
+ * Halfway values are rounded away from zero.
+ * For example `[\"round\", -1.5]` evaluates to -2.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(round(3.14159265359f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number number to round
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-round">Style specification</a>
+ */
+ public static Expression round(Number number) {
+ return round(literal(number));
+ }
+
+ /**
+ * Returns the absolute value of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(abs(subtract(pi())))
+ * );
+ * }
+ * </pre>
+ *
+ * @param expression number expression to get absolute value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression abs(Expression expression) {
+ return new Expression("abs", expression);
+ }
+
+ /**
+ * Returns the absolute value of the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(abs(-3.14159265359f))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number number to get absolute value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression abs(Number number) {
+ return abs(literal(number));
+ }
+
+ /**
+ * Returns the smallest integer that is greater than or equal to the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(ceil(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param expression number expression to get value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression ceil(Expression expression) {
+ return new Expression("ceil", expression);
+ }
+
+ /**
+ * Returns the smallest integer that is greater than or equal to the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(ceil(3.14159265359))
+ * );
+ * }
+ * </pre>
+ * @param number number to get value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression ceil(Number number) {
+ return ceil(literal(number));
+ }
+
+ /**
+ * Returns the largest integer that is less than or equal to the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(floor(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param expression number expression to get value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression floor(Expression expression) {
+ return new Expression("floor", expression);
+ }
+
+ /**
+ * Returns the largest integer that is less than or equal to the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(floor(pi()))
+ * );
+ * }
+ * </pre>
+ *
+ * @param number number to get value from
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-abs">Style specification</a>
+ */
+ public static Expression floor(Number number) {
+ return floor(literal(number));
+ }
+
+ /**
+ * 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(upcase(get("key-to-string-value"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param string the string to upcase
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-upcase">Style specification</a>
+ */
+ public static Expression upcase(@NonNull Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(upcase("text"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param string string to upcase
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-upcase">Style specification</a>
+ */
+ public static Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(downcase(get("key-to-string-value"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-downcase">Style specification</a>
+ */
+ public static Expression downcase(@NonNull Expression 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(upcase("key-to-string-value")
+ * );
+ * }
+ * </pre>
+ *
+ * @param input string to downcase
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-downcase">Style specification</a>
+ */
+ public static Expression downcase(@NonNull String input) {
+ return downcase(literal(input));
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(concat(get("key-to-string-value"), literal("other string"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-concat">Style specification</a>
+ */
+ public static Expression concat(@NonNull Expression... input) {
+ return new Expression("concat", input);
+ }
+
+ /**
+ * Returns a string consisting of the concatenation of the inputs.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(concat("foo", "bar"))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-concat">Style specification</a>
+ */
+ public static Expression concat(@NonNull String... input) {
+ Expression[] stringExpression = new Expression[input.length];
+ for (int i = 0; i < input.length; i++) {
+ stringExpression[i] = literal(input[i]);
+ }
+ return concat(stringExpression);
+ }
+
+ /**
+ * 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-array">Style specification</a>
+ */
+ public static Expression 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-typeof">Style specification</a>
+ */
+ public static Expression 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.
+ * The asserted input value is returned as result.
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-string">Style specification</a>
+ */
+ public static Expression string(@NonNull Expression... input) {
+ 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.
+ * The asserted input value is returned as result.
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-number">Style specification</a>
+ */
+ public static Expression number(@NonNull Expression... input) {
+ 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.
+ * The asserted input value is returned as result.
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-boolean">Style specification</a>
+ */
+ public static Expression bool(@NonNull Expression... input) {
+ return new Expression("boolean", input);
+ }
+
+ /**
+ * Asserts that the input value is an object. If it is not, the expression is an error
+ * The asserted input value is returned as result.
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-object">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * SymbolLayer symbolLayer = new SymbolLayer("layer-id", "source-id");
+ * symbolLayer.setProperties(
+ * textField(toString(get("key-to-number-value")))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-string">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(toNumber(get("key-to-string-value")))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-number">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(toBool(get("key-to-value"));
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-boolean">Style specification</a>
+ */
+ public static Expression 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new FillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(toColor(get("keyStringValue")))
+ * );
+ * }
+ * </pre>
+ *
+ * @param input expression input
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-color">Style specification</a>
+ */
+ public static Expression toColor(@NonNull Expression input) {
+ return new Expression("to-color", input);
+ }
+
+ /**
+ * 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-let">Style specification</a>
+ */
+ 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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-var">Style specification</a>
+ */
+ public static Expression var(@NonNull Expression expression) {
+ return new Expression("var", expression);
+ }
+
+ /**
+ * References variable bound using let.
+ *
+ * @param variableName the variable naming that was bound with using let
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-var">Style specification</a>
+ */
+ public static Expression var(@NonNull String variableName) {
+ return var(literal(variableName));
+ }
+
+ /**
+ * 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>
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * exponential(0.5f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-zoom">Style specification</a>
+ */
+ public static Expression zoom() {
+ return new Expression("zoom");
+ }
+
+ /**
+ * Produces a stop value to be used as part of the step expression.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), literal(0.0f),
+ * stop(1.0f, 2.5f),
+ * stop(10.0f, 5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param stop the stop input
+ * @param value the stop output
+ * @return the stop
+ */
+ 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), literal(0.0f),
+ * literal(1.0f), literal(2.5f),
+ * literal(10.0f), literal(5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Number input, @NonNull Expression defaultOutput, Expression... stops) {
+ return step(literal(input), defaultOutput, 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), literal(0.0f),
+ * literal(1.0f), literal(2.5f),
+ * literal(10.0f), literal(5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input expression
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Expression input, @NonNull Expression defaultOutput, Expression... stops) {
+ return new Expression("step", join(new Expression[] {input, defaultOutput}, 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), literal(0.0f),
+ * stop(1, 2.5f),
+ * stop(10, 5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Number input, @NonNull Expression defaultOutput, Stop... stops) {
+ return step(literal(input), defaultOutput, Stop.toExpressionArray(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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), literal(0.0f),
+ * stop(1, 2.5f),
+ * stop(10, 5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Expression input, @NonNull Expression defaultOutput, Stop... stops) {
+ return step(input, defaultOutput, Stop.toExpressionArray(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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(1.0f, 0.0f,
+ * literal(1.0f), literal(2.5f),
+ * literal(10.0f), literal(5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Number input, @NonNull Number defaultOutput, Expression... stops) {
+ return step(literal(input), defaultOutput, 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), 0.0f,
+ * literal(1.0f), literal(2.5f),
+ * literal(10.0f), literal(5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input expression
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Expression input, @NonNull Number defaultOutput, Expression... stops) {
+ return step(input, literal(defaultOutput), 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), 0.0f,
+ * stop(1, 2.5f),
+ * stop(10, 5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Number input, @NonNull Number defaultOutput, Stop... stops) {
+ return step(literal(input), defaultOutput, Stop.toExpressionArray(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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * CircleLayer circleLayer = new CircleLayer("layer-id", "source-id");
+ * circleLayer.setProperties(
+ * circleRadius(
+ * step(zoom(), 0.0f,
+ * stop(1, 2.5f),
+ * stop(10, 5.0f)
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param input the input value
+ * @param defaultOutput the default output expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step">Style specification</a>
+ */
+ public static Expression step(@NonNull Expression input, @NonNull Number defaultOutput, Stop... stops) {
+ return step(input, defaultOutput, Stop.toExpressionArray(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`.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * exponential(0.5f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static Expression interpolate(@NonNull Interpolator interpolation,
+ @NonNull Expression 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`.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * exponential(0.5f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param interpolation type of interpolation
+ * @param number the input expression
+ * @param stops pair of input and output values
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static Expression interpolate(@NonNull Interpolator interpolation,
+ @NonNull Expression number, Stop... stops) {
+ return interpolate(interpolation, number, Stop.toExpressionArray(stops));
+ }
+
+ /**
+ * interpolates linearly between the pair of stops just less than and just greater than the input.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * linear(), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static Interpolator linear() {
+ return new Interpolator("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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * exponential(0.5f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param base value controlling the route at which the output increases
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static 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.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * exponential(get("keyToValue"), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @param expression base number expression
+ * @return expression
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static Interpolator exponential(@NonNull Expression expression) {
+ return new Interpolator("exponential", expression);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * cubicBezier(0.42f, 0.0f, 1.0f, 1.0f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static Interpolator cubicBezier(@NonNull Expression x1, @NonNull Expression y1,
+ @NonNull Expression x2, @NonNull Expression y2) {
+ return new Interpolator("cubic-bezier", x1, y1, x2, y2);
+ }
+
+ /**
+ * Interpolates using the cubic bezier curve defined by the given control points.
+ * <p>
+ * Example usage:
+ * </p>
+ * <pre>
+ * {@code
+ * FillLayer fillLayer = new fillLayer("layer-id", "source-id");
+ * fillLayer.setProperties(
+ * fillColor(
+ * interpolate(
+ * cubicBezier(0.42f, 0.0f, 1.0f, 1.0f), zoom(),
+ * literal(1.0f), color(Color.RED),
+ * literal(5.0f, color(Color.BLUE),
+ * literal(10.0f, color(Color.GREEN)
+ * )
+ * )
+ * );
+ * }
+ * </pre>
+ *
+ * @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
+ * @see <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate">Style specification</a>
+ */
+ public static 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;
+ }
+
+ /**
+ * 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 PropertyValue) {
+ throw new IllegalArgumentException("PropertyValue are not allowed as an expression literal, use value instead.");
+ } else if (value instanceof Expression.ExpressionLiteral) {
+ return toValue((ExpressionLiteral) value);
+ } else if (value instanceof Expression) {
+ return ((Expression) value).toArray();
+ }
+ return value;
+ }
+
+ /**
+ * Returns a string representation of the object that matches the definition set in the style specification.
+ *
+ * @return a string representation of the object.
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("[\"").append(operator).append("\"");
+ if (arguments != null) {
+ for (Object argument : arguments) {
+ builder.append(", ");
+ if (argument instanceof ExpressionLiteral) {
+ builder.append(((ExpressionLiteral) argument).toValue());
+ } else {
+ builder.append(argument.toString());
+ }
+ }
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param o the other object
+ * @return true if equal, false if not
+ */
+ @Override
+ public boolean equals(Object o) {
+ super.equals(o);
+ if (this == o) {
+ return true;
+ }
+
+ if (o == null || !(o instanceof Expression)) {
+ return false;
+ }
+
+ Expression that = (Expression) o;
+
+ if (operator != null ? !operator.equals(that.operator) : that.operator != null) {
+ return false;
+ }
+ return Arrays.deepEquals(arguments, that.arguments);
+ }
+
+ /**
+ * Returns a hash code value for the expression.
+ *
+ * @return a hash code value for this expression
+ */
+ @Override
+ public int hashCode() {
+ int result = operator != null ? operator.hashCode() : 0;
+ result = 31 * result + Arrays.hashCode(arguments);
+ return result;
+ }
+
+ /**
+ * 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>
+ */
+ public static class ExpressionLiteral extends Expression {
+
+ protected Object literal;
+
+ /**
+ * Create an expression literal.
+ *
+ * @param object the object to be treated as literal
+ */
+ public ExpressionLiteral(@NonNull Object object) {
+ this.literal = object;
+ }
+
+ /**
+ * Get the literal object.
+ *
+ * @return the literal object
+ */
+ Object toValue() {
+ return literal;
+ }
+
+ /**
+ * Returns a string representation of the expression literal.
+ *
+ * @return a string representation of the object.
+ */
+ @Override
+ public String toString() {
+ return literal.toString();
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param o the other object
+ * @return true if equal, false if not
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ ExpressionLiteral that = (ExpressionLiteral) o;
+
+ return literal != null ? literal.equals(that.literal) : that.literal == null;
+ }
+
+ /**
+ * Returns a hash code value for the expression literal.
+ *
+ * @return a hash code value for this expression literal
+ */
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (literal != null ? literal.hashCode() : 0);
+ return result;
+ }
+ }
+
+ /**
+ * Expression interpolator type.
+ * <p>
+ * Is used for first parameter of {@link #interpolate(Interpolator, Expression, Stop...)}.
+ * </p>
+ */
+ public static class Interpolator extends Expression {
+
+ Interpolator(@NonNull String operator, @Nullable Expression... arguments) {
+ super(operator, arguments);
+ }
+ }
+
+ /**
+ * 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(Interpolator, Expression, Stop...)}.
+ * </p>
+ */
+ public static class Stop {
+
+ private Object value;
+ private Object output;
+
+ Stop(Object value, Object output) {
+ this.value = value;
+ this.output = output;
+ }
+
+ /**
+ * Converts a varargs of Stops to a Expression array.
+ *
+ * @param stops the stops to convert
+ * @return the converted stops as an expression array
+ */
+ static Expression[] toExpressionArray(Stop... stops) {
+ Expression[] expressions = new Expression[stops.length * 2];
+ Stop stop;
+ Object inputValue, outputValue;
+ for (int i = 0; i < stops.length; i++) {
+ stop = stops[i];
+ inputValue = stop.value;
+ outputValue = stop.output;
+
+ if (!(inputValue instanceof Expression)) {
+ inputValue = literal(inputValue);
+ }
+
+ if (!(outputValue instanceof Expression)) {
+ outputValue = literal(outputValue);
+ }
+
+ expressions[i * 2] = (Expression) inputValue;
+ expressions[i * 2 + 1] = (Expression) outputValue;
+ }
+ return expressions;
+ }
+ }
+
+ /**
+ * Converts a JsonArray to an expression.
+ */
+ public final static class Converter {
+
+ /**
+ * Converts a JsonArray to an expression
+ *
+ * @param jsonArray the json array to convert
+ * @return the expression
+ */
+ public static Expression convert(@NonNull JsonArray jsonArray) {
+ if (jsonArray.size() == 0) {
+ throw new IllegalArgumentException("Can't convert empty jsonArray expressions");
+ }
+
+ final String operator = jsonArray.get(0).getAsString();
+ final List<Expression> arguments = new ArrayList<>();
+
+ JsonElement jsonElement;
+ for (int i = 1; i < jsonArray.size(); i++) {
+ jsonElement = jsonArray.get(i);
+ if (jsonElement instanceof JsonArray) {
+ arguments.add(convert((JsonArray) jsonElement));
+ } else if (jsonElement instanceof JsonPrimitive) {
+ arguments.add(convert((JsonPrimitive) jsonElement));
+ } else {
+ throw new RuntimeException("Unsupported expression conversion for " + jsonElement.getClass());
+ }
+ }
+ return new Expression(operator, arguments.toArray(new Expression[arguments.size()]));
+ }
+
+ /**
+ * Converts a JsonPrimitive to an expression literal
+ *
+ * @param jsonPrimitive the json primitive to convert
+ * @return the expression literal
+ */
+ private static Expression convert(@NonNull JsonPrimitive jsonPrimitive) {
+ if (jsonPrimitive.isBoolean()) {
+ return new Expression.ExpressionLiteral(jsonPrimitive.getAsBoolean());
+ } else if (jsonPrimitive.isNumber()) {
+ return new Expression.ExpressionLiteral(jsonPrimitive.getAsFloat());
+ } else if (jsonPrimitive.isString()) {
+ return new Expression.ExpressionLiteral(jsonPrimitive.getAsString());
+ } else {
+ throw new RuntimeException("Unsupported literal expression conversion for " + jsonPrimitive.getClass());
+ }
+ }
+ }
+
+ /**
+ * Expression to wrap Object[] as a literal
+ */
+ private static class ExpressionArray extends Expression {
+
+ private Object[] array;
+
+ ExpressionArray(Object[] array) {
+ this.array = array;
+ }
+
+ @NonNull
+ @Override
+ public Object[] toArray() {
+ return new Object[] {
+ "literal", array
+ };
+ }
+
+ /**
+ * Convert the expression array to a string representation.
+ *
+ * @return the string representation of the expression array
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[\"literal\"], [");
+ Object argument;
+ for (int i = 0; i < array.length; i++) {
+ argument = array[i];
+ if (argument instanceof String) {
+ builder.append("\"").append(argument).append("\"");
+ } else {
+ builder.append(argument);
+ }
+
+ if (i != array.length - 1) {
+ builder.append(", ");
+ }
+ }
+ builder.append("]]");
+ return builder.toString();
+ }
+ }
+
+ /**
+ * Converts an object that is a primitive array to an Object[]
+ *
+ * @param object the object to convert to an object array
+ * @return the converted object array
+ */
+ static Object[] toObjectArray(Object object) {
+ // object is a primitive array
+ int len = java.lang.reflect.Array.getLength(object);
+ Object[] objects = new Object[len];
+ for (int i = 0; i < len; i++) {
+ objects[i] = java.lang.reflect.Array.get(object, i);
+ }
+ return objects;
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java
new file mode 100644
index 0000000000..ee2b96fa61
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android Expression API classes.
+ */
+package com.mapbox.mapboxsdk.style.expressions;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java
deleted file mode 100644
index bb87fe8a39..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CameraFunction.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions;
-
-import android.support.annotation.Keep;
-import android.support.annotation.NonNull;
-
-import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
-import com.mapbox.mapboxsdk.style.functions.stops.IntervalStops;
-import com.mapbox.mapboxsdk.style.functions.stops.Stop;
-import com.mapbox.mapboxsdk.style.functions.stops.Stops;
-
-/**
- * Camera function. Functions that take camera properties as input (zoom for now)
- * <p>
- * Zoom functions allow the appearance of a map feature to change with map’s zoom level.
- * Zoom functions can be used to create the illusion of depth and control data density.
- * Each stop is an array with two elements: the first is a zoom level and the second is
- * a function output value.
- *
- * @param <I> the input type
- * @param <O> the output type
- * @see Function#zoom
- */
-public class CameraFunction<I extends Number, O> extends Function<I, O> {
-
- /**
- * Create an exponential camera function
- *
- * @param stops @see {@link com.mapbox.mapboxsdk.style.functions.stops.Stops#exponential(float, Stop[])}
- */
- CameraFunction(@NonNull ExponentialStops<I, O> stops) {
- super(stops);
- }
-
- /**
- * Create an interval camera function
- *
- * @param stops @see {@link com.mapbox.mapboxsdk.style.functions.stops.Stops#interval(Stop[])}
- */
- CameraFunction(@NonNull IntervalStops<I, O> stops) {
- super(stops);
- }
-
- /**
- * JNI constructor
- */
- @Keep
- private CameraFunction(Stops<I, O> stops) {
- super(stops);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java
deleted file mode 100644
index 15e4474105..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/CompositeFunction.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.style.functions.stops.CategoricalStops;
-import com.mapbox.mapboxsdk.style.functions.stops.ExponentialStops;
-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.PropertyValue;
-
-import java.util.Map;
-
-/**
- * Composite functions combine Camera and SourceFunctions.
- * <p>
- * Composite functions allow the appearance of a map feature to change with both its
- * properties and zoom. Each stop is an array with two elements, the first is an object
- * with a property input value and a zoom, and the second is a function output value. Note
- * that support for property functions is not yet complete.
- *
- * @param <Z> the zoom type (usually Float)
- * @param <I> the input type (the feature property type)
- * @param <O> the output type (the property type)
- * @see Function#composite
- */
-public class CompositeFunction<Z extends Number, I, O> extends Function<Stop.CompositeValue<Z, I>, O> {
-
- private final String property;
- private PropertyValue<O> defaultValue;
-
- CompositeFunction(@NonNull String property,
- @NonNull CategoricalStops<Stop.CompositeValue<Z, I>, O> stops) {
- this(null, property, stops);
- }
-
- CompositeFunction(@NonNull String property,
- @NonNull ExponentialStops<Stop.CompositeValue<Z, I>, O> stops) {
- this(null, property, stops);
- }
-
- CompositeFunction(@NonNull String property,
- @NonNull IntervalStops<Stop.CompositeValue<Z, I>, O> stops) {
- this(null, property, stops);
- }
-
-
- /**
- * JNI Constructor
- */
- private CompositeFunction(@Nullable O defaultValue, @NonNull String property,
- @NonNull Stops<Stop.CompositeValue<Z, I>, O> stops) {
- super(stops);
- this.defaultValue = new PropertyValue<>(property, defaultValue);
- this.property = property;
- }
-
- /**
- * Set the default value
- *
- * @param defaultValue the default value to use when no other applies
- * @return this (for chaining)
- */
- public CompositeFunction<Z, I, O> withDefaultValue(PropertyValue<O> defaultValue) {
- this.defaultValue = defaultValue;
- return this;
- }
-
- /**
- * @return the defaultValue
- */
- @Nullable
- public PropertyValue<O> getDefaultValue() {
- return defaultValue;
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return the feature property name
- */
- public String getProperty() {
- return property;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Map<String, Object> toValueObject() {
- Map<String, Object> valueObject = super.toValueObject();
- valueObject.put(PROPERTY_KEY, property);
- if (defaultValue != null) {
- valueObject.put(DEFAULT_VALUE_KEY, defaultValue.value);
- }
- return valueObject;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java
deleted file mode 100644
index e7bb52ebb3..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/Function.java
+++ /dev/null
@@ -1,300 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-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 java.util.Map;
-
-import timber.log.Timber;
-
-/**
- * Functions are used to change properties in relation to the state of the map.
- * <p>
- * The value for any layout or paint property may be specified as a function. Functions allow you to
- * make the appearance of a map feature change with the current zoom level and/or the feature's properties.
- *
- * @param <I> the function's input type
- * @param <O> the target property's value type. Make sure it matches.
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">The style specification</a>
- */
-public class Function<I, O> {
-
- static final String PROPERTY_KEY = "property";
- static final String DEFAULT_VALUE_KEY = "default";
-
- /**
- * Create an exponential {@link CameraFunction}
- * <p>
- * Zoom functions allow the appearance of a map feature to change with map’s zoom.
- * Zoom functions can be used to create the illusion of depth and control data density.
- * Each stop is an array with two elements, the first is a zoom and the second is a function output value.
- *
- * @param <Z> the zoom level type (Float, Integer)
- * @param <O> the property type
- * @param stops the stops implementation that define the function. @see {@link Stops#exponential(Stop[])}
- * @return the {@link CameraFunction}
- * @see CameraFunction
- * @see ExponentialStops
- * @see Stops#exponential(Stop[])
- * @see Stops#exponential(Stop[])
- * @see Stop#stop
- */
- public static <Z extends Number, O> CameraFunction<Z, O> zoom(@NonNull ExponentialStops<Z, O> stops) {
- return new CameraFunction<>(stops);
- }
-
- /**
- * Create an interval {@link CameraFunction}
- * <p>
- * Zoom functions allow the appearance of a map feature to change with map’s zoom.
- * Zoom functions can be used to create the illusion of depth and control data density.
- * Each stop is an array with two elements, the first is a zoom and the second is a function output value.
- *
- * @param <Z> the zoom level type (Float, Integer)
- * @param <O> the property type
- * @param stops the stops implementation that define the function. @see {@link Stops#interval(Stop[])}
- * @return the {@link CameraFunction}
- * @see CameraFunction
- * @see IntervalStops
- * @see Stops#interval(Stop[])
- * @see Stop#stop
- */
- public static <Z extends Number, O> CameraFunction<Z, O> zoom(@NonNull IntervalStops<Z, O> stops) {
- return new CameraFunction<>(stops);
- }
-
- /**
- * Create an exponential {@link SourceFunction}
- * <p>
- * Source functions allow the appearance of a map feature to change with
- * its properties. Source functions can be used to visually differentiate
- * types of features within the same layer or create data visualizations.
- * Each stop is an array with two elements, the first is a property input
- * value and the second is a function output value. Note that support for
- * property functions is not available across all properties and platforms
- * at this time.
- *
- * @param property the feature property name
- * @param stops the stops
- * @param <I> the function input type
- * @param <O> the function output type
- * @return the {@link SourceFunction}
- * @see SourceFunction
- * @see ExponentialStops
- * @see Stops#exponential(Stop[])
- * @see Stop#stop
- */
- public static <I, O> SourceFunction<I, O> property(@NonNull String property, @NonNull ExponentialStops<I, O> stops) {
- return new SourceFunction<>(property, stops);
- }
-
- /**
- * Create an identity {@link SourceFunction}
- * <p>
- * Source functions allow the appearance of a map feature to change with
- * its properties. Source functions can be used to visually differentiate
- * types of features within the same layer or create data visualizations.
- * Each stop is an array with two elements, the first is a property input
- * value and the second is a function output value. Note that support for
- * property functions is not available across all properties and platforms
- * at this time.
- *
- * @param property the feature property name
- * @param stops the stops
- * @param <T> the function input/output type
- * @return the {@link SourceFunction}
- * @see SourceFunction
- * @see IdentityStops
- * @see Stops#identity()
- * @see Stop#stop
- */
- public static <T> SourceFunction<T, T> property(@NonNull String property, @NonNull IdentityStops<T> stops) {
- return new SourceFunction<>(property, stops);
- }
-
- /**
- * Create an interval {@link SourceFunction}
- * <p>
- * Source functions allow the appearance of a map feature to change with
- * its properties. Source functions can be used to visually differentiate
- * types of features within the same layer or create data visualizations.
- * Each stop is an array with two elements, the first is a property input
- * value and the second is a function output value. Note that support for
- * property functions is not available across all properties and platforms
- * at this time.
- *
- * @param property the feature property name
- * @param stops the stops
- * @param <I> the function input type
- * @param <O> the function output type
- * @return the {@link SourceFunction}
- * @see SourceFunction
- * @see IntervalStops
- * @see Stops#interval(Stop[])
- * @see Stop#stop
- */
- public static <I, O> SourceFunction<I, O> property(@NonNull String property, @NonNull IntervalStops<I, O> stops) {
- return new SourceFunction<>(property, stops);
- }
-
- /**
- * Create an categorical {@link SourceFunction}
- * <p>
- * Source functions allow the appearance of a map feature to change with
- * its properties. Source functions can be used to visually differentiate
- * types of features within the same layer or create data visualizations.
- * Each stop is an array with two elements, the first is a property input
- * value and the second is a function output value. Note that support for
- * property functions is not available across all properties and platforms
- * at this time.
- *
- * @param property the feature property name
- * @param stops the stops
- * @param <I> the function input type
- * @param <O> the function output type
- * @return the {@link SourceFunction}
- * @see SourceFunction
- * @see CategoricalStops
- * @see Stops#categorical(Stop[])
- * @see Stop#stop
- */
- public static <I, O> SourceFunction<I, O> property(@NonNull String property, @NonNull CategoricalStops<I, O> stops) {
- return new SourceFunction<>(property, stops);
- }
-
- /**
- * Create a composite, categorical function.
- * <p>
- * Composite functions allow the appearance of a map feature to change with both its
- * properties and zoom. Each stop is an array with two elements, the first is an object
- * with a property input value and a zoom, and the second is a function output value. Note
- * that support for property functions is not yet complete.
- *
- * @param property the feature property name for the source part of the function
- * @param stops the stops
- * @param <Z> the zoom function input type (Float usually)
- * @param <I> the function input type for the source part of the function
- * @param <O> the function output type
- * @return the {@link CompositeFunction}
- * @see CompositeFunction
- * @see CategoricalStops
- * @see Stops#categorical(Stop[])
- * @see Stop#stop
- */
- public static <Z extends Number, I, O> CompositeFunction<Z, I, O> composite(
- @NonNull String property,
- @NonNull CategoricalStops<Stop.CompositeValue<Z, I>, O> stops) {
-
- return new CompositeFunction<>(property, stops);
- }
-
- /**
- * Create a composite, exponential function.
- * <p>
- * Composite functions allow the appearance of a map feature to change with both its
- * properties and zoom. Each stop is an array with two elements, the first is an object
- * with a property input value and a zoom, and the second is a function output value. Note
- * that support for property functions is not yet complete.
- *
- * @param property the feature property name for the source part of the function
- * @param stops the stops
- * @param <Z> the zoom function input type (Float usually)
- * @param <I> the function input type for the source part of the function
- * @param <O> the function output type
- * @return the {@link CompositeFunction}
- * @see CompositeFunction
- * @see ExponentialStops
- * @see Stops#exponential(Stop[])
- * @see Stop#stop
- */
- public static <Z extends Number, I, O> CompositeFunction<Z, I, O> composite(
- @NonNull String property,
- @NonNull ExponentialStops<Stop.CompositeValue<Z, I>, O> stops) {
-
- return new CompositeFunction<>(property, stops);
- }
-
- /**
- * Create a composite, interval function.
- * <p>
- * Composite functions allow the appearance of a map feature to change with both its
- * properties and zoom. Each stop is an array with two elements, the first is an object
- * with a property input value and a zoom, and the second is a function output value. Note
- * that support for property functions is not yet complete.
- *
- * @param property the feature property name for the source part of the function
- * @param stops the stops
- * @param <Z> the zoom function input type (Float usually)
- * @param <I> the function input type for the source part of the function
- * @param <O> the function output type
- * @return the {@link CompositeFunction}
- * @see CompositeFunction
- * @see IntervalStops
- * @see Stops#interval(Stop[])
- * @see Stop#stop
- */
- public static <Z extends Number, I, O> CompositeFunction<Z, I, O> composite(
- @NonNull String property,
- @NonNull IntervalStops<Stop.CompositeValue<Z, I>, O> stops) {
-
- return new CompositeFunction<>(property, stops);
- }
-
- // Class definition //
-
- private final Stops<I, O> stops;
-
- /**
- * JNI Cosntructor for implementation classes
- *
- * @param stops the stops
- */
- Function(@NonNull Stops<I, O> stops) {
- this.stops = stops;
- }
-
- /**
- * @return the stops in this function
- */
- public Stops getStops() {
- return stops;
- }
-
- /**
- * Convenience method
- *
- * @param <S> the Stops implementation type
- * @return the Stops implementation or null when the wrong type is specified
- */
- @Nullable
- public <S extends Stops> S getStopsAs() {
- try {
- // noinspection unchecked
- return (S) stops;
- } catch (ClassCastException exception) {
- Timber.e(exception, "Stops: %s is a different type: ", stops.getClass());
- return null;
- }
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return a value object representation for core conversion
- */
- public Map<String, Object> toValueObject() {
- return stops.toValueObject();
- }
-
- @Override
- public String toString() {
- return String.format("%s: %s", getClass().getSimpleName(), stops);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/SourceFunction.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/SourceFunction.java
deleted file mode 100644
index 33f436ae71..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/SourceFunction.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.mapbox.mapboxsdk.style.functions.stops.Stops;
-import com.mapbox.mapboxsdk.style.layers.PropertyValue;
-
-import java.util.Map;
-
-/**
- * Source functions take Feature property names as input.
- * <p>
- * Source functions allow the appearance of a map feature to change with
- * its properties. Source functions can be used to visually differentiate
- * types of features within the same layer or create data visualizations.
- * Each stop is an array with two elements, the first is a property input
- * value and the second is a function output value. Note that support for
- * property functions is not available across all properties and platforms
- * at this time.
- *
- * @param <I> the input type
- * @param <O> the output type
- * @see Function#property
- */
-public class SourceFunction<I, O> extends Function<I, O> {
-
- private final String property;
- private PropertyValue<O> defaultValue;
-
- SourceFunction(@NonNull String property, @NonNull Stops<I, O> stops) {
- this(null, property, stops);
- }
-
- /**
- * JNI Constructor
- */
- private SourceFunction(@Nullable O defaultValue, @NonNull String property, @NonNull Stops<I, O> stops) {
- super(stops);
- this.property = property;
- this.defaultValue = defaultValue != null ? new PropertyValue<>(property, defaultValue) : null;
- }
-
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return The feature property name
- */
- public String getProperty() {
- return property;
- }
-
- /**
- * Set the default value
- *
- * @param defaultValue the default value to use when no other applies
- * @return this (for chaining)
- */
- public SourceFunction<I, O> withDefaultValue(PropertyValue<O> defaultValue) {
- this.defaultValue = defaultValue;
- return this;
- }
-
- /**
- * @return the defaultValue
- */
- @Nullable
- public PropertyValue<O> getDefaultValue() {
- return defaultValue;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Map<String, Object> toValueObject() {
- Map<String, Object> valueObject = super.toValueObject();
- valueObject.put(PROPERTY_KEY, property);
- if (defaultValue != null) {
- valueObject.put(DEFAULT_VALUE_KEY, defaultValue.value);
- }
- return valueObject;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/package-info.java
deleted file mode 100644
index 6979676c9e..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * Contains the Mapbox Maps Android Style Function API classes.
- *
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">The style specification</a>
- */
-package com.mapbox.mapboxsdk.style.functions;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops.java
deleted file mode 100644
index f9b2929350..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Size;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * {@link Stops} implementation for categorical functions
- *
- * @param <I> the {@link Stop} input type
- * @param <O> the {@link Stop} output type
- */
-public class CategoricalStops<I, O> extends IterableStops<I, O, Stop<I, O>> {
-
- private final Stop<I, O>[] stops;
-
- /**
- * Create a categorical {@link Stops} implementation. Use through {@link Stops#categorical(Stop[])}
- *
- * @param stops the stops
- */
- @SafeVarargs
- public CategoricalStops(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- this.stops = stops;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Map<String, Object> toValueObject() {
- Map<String, Object> map = super.toValueObject();
- map.put("stops", toValueObjects(stops));
- return map;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getTypeName() {
- return "categorical";
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Iterator<Stop<I, O>> iterator() {
- return Arrays.asList(stops).iterator();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- return stops.length;
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops.java
deleted file mode 100644
index d47aa1fc91..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Size;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * The {@link Stops} implementation for exponential functions
- *
- * @param <I> the input type
- * @param <O> the output type
- */
-public class ExponentialStops<I, O> extends IterableStops<I, O, Stop<I, O>> {
-
- private float base;
- private final Stop<I, O>[] stops;
-
- /**
- * Create exponential stops with an explicit base. Use through {@link Stops#exponential(Stop[])}
- *
- * @param base The exponential base of the interpolation curve. It controls the rate at which the function 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 stops the stops
- */
- @SafeVarargs
- public ExponentialStops(Float base, @NonNull @Size(min = 1) Stop<I, O>... stops) {
- this.base = base != null ? base : 1.0f;
- this.stops = stops;
- }
-
- /**
- * Create exponential stops without an explicit base. Use through {@link Stops#exponential(Stop[])}
- *
- * @param stops the stops
- */
- @SafeVarargs
- public ExponentialStops(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- this(null, stops);
- }
-
- /**
- * Set the exponential base
- *
- * @param base the base to use in the exponential function
- * @return this (for chaining)
- */
- public ExponentialStops<I, O> withBase(float base) {
- this.base = base;
- return this;
- }
-
- /**
- * @return The exponential base
- */
- public float getBase() {
- return base;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Map<String, Object> toValueObject() {
- Map<String, Object> map = super.toValueObject();
- map.put("base", base);
- map.put("stops", toValueObjects(stops));
- return map;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getTypeName() {
- return "exponential";
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Iterator<Stop<I, O>> iterator() {
- return Arrays.asList(stops).iterator();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- return stops.length;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IdentityStops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IdentityStops.java
deleted file mode 100644
index 2c0b198dc2..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IdentityStops.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-/**
- * The {@link Stops} implementation for identity functions
- *
- * @param <T> the input/output type
- */
-public class IdentityStops<T> extends Stops<T, T> {
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected String getTypeName() {
- return "identity";
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IntervalStops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IntervalStops.java
deleted file mode 100644
index 9d95b3f8c1..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IntervalStops.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import android.support.annotation.NonNull;
-import android.support.annotation.Size;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * The {@link Stops} implementation for interval functions
- *
- * @param <I> the input type
- * @param <O> the output type
- */
-public class IntervalStops<I, O> extends IterableStops<I, O, Stop<I, O>> {
-
- private final Stop<I, O>[] stops;
-
- @SafeVarargs
- public IntervalStops(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- this.stops = stops;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getTypeName() {
- return "interval";
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Map<String, Object> toValueObject() {
- Map<String, Object> map = super.toValueObject();
- map.put("stops", toValueObjects(stops));
- return map;
- }
-
- /**
- * @return an {@link Iterator} for the contained stops
- */
- @Override
- public Iterator<Stop<I, O>> iterator() {
- return Arrays.asList(stops).iterator();
- }
-
- /**
- * @return The number of contained stops
- */
- @Override
- public int size() {
- return stops.length;
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IterableStops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IterableStops.java
deleted file mode 100644
index 8c5a6e8913..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/IterableStops.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import java.util.Iterator;
-
-/**
- * Base class for {@link Stops} implementations with a collection of stops
- *
- * @param <I> the {@link Stops} input type
- * @param <O> the {@link Stops} output type
- * @param <S> the {@link Iterable} element type (usually {@link Stop})
- */
-public abstract class IterableStops<I, O, S> extends Stops<I, O> implements Iterable<S> {
-
- /**
- * @return The size of the contained stops collection
- */
- public abstract int size();
-
- /**
- * Convenience function to toValueObjects an array of stops to an array of value objects
- *
- * @param stops the stops to toValueObjects
- * @return the stops as value objects
- */
- Object[] toValueObjects(Stop<I, O>[] stops) {
- if (stops != null) {
- Object[] stopsValue = new Object[stops.length];
-
- for (int i = 0; i < stopsValue.length; i++) {
- Stop stop = stops[i];
- stopsValue[i] = stop.toValueObject();
- }
- return stopsValue;
- }
-
- return null;
- }
-
- @Override
- public String toString() {
- StringBuilder buffer = new StringBuilder();
- Iterator<S> iterator = iterator();
- while (iterator.hasNext()) {
- S stop = iterator.next();
- buffer.append(stop);
- if (iterator.hasNext()) {
- buffer.append(",");
- }
- }
- return String.format("%s: [%s]", super.toString(), buffer.toString());
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stop.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stop.java
deleted file mode 100644
index 72164f4c4b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stop.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import com.mapbox.mapboxsdk.style.functions.Function;
-import com.mapbox.mapboxsdk.style.layers.PropertyValue;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A stop represents a certain point in the range of this function
- *
- * @param <I> input the stop (function) input type
- * @param <O> output the stop (function) output type
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">The style specification</a>
- */
-public class Stop<I, O> {
- /**
- * Creates a {@link Stop} to use in a {@link Function}
- *
- * @param in the input for the stop
- * @param output the output for the stop
- * @param <I> the input property type
- * @param <O> the output property type
- * @return the {@link Stop}
- */
- public static <I, O> Stop<I, O> stop(I in, PropertyValue<O> output) {
- return new Stop<>(in, output.value);
- }
-
- /**
- * Create a composite {@link Stop} for use in a {@link com.mapbox.mapboxsdk.style.functions.CompositeFunction}
- *
- * @param zoom the zoom input
- * @param value the feature property input
- * @param output the output for the stop
- * @param <Z> the zoom type
- * @param <I> the feature property input type
- * @param <O> the output property type
- * @return the {@link Stop}
- * @see Function#composite(String, ExponentialStops)
- */
- public static <Z extends Number, I, O> Stop<Stop.CompositeValue<Z, I>, O> stop(Z zoom,
- I value,
- PropertyValue<O> output) {
- return new Stop<>(new Stop.CompositeValue<>(zoom, value), output.value);
- }
-
- /**
- * Represents a composite input value for composite functions (eg zoom and feature property value)
- *
- * @param <Z> the zoom input type (typically Float)
- * @param <V> the feature property input type
- */
- public static class CompositeValue<Z extends Number, V> {
- public final Z zoom;
- public final V value;
-
- CompositeValue(Z zoom, V value) {
- this.zoom = zoom;
- this.value = value;
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return the value object representation for core conversion
- */
- Map<String, Object> toValueObject() {
- HashMap<String, Object> map = new HashMap<>();
- map.put("zoom", zoom);
- map.put("value", value);
- return map;
- }
-
- @Override
- public String toString() {
- return String.format("[zoom: %s, value: %s]", zoom, value);
- }
- }
-
- /**
- * The input type
- */
- public final I in;
-
- /**
- * The output type
- */
- public final O out;
-
- Stop(I in, O out) {
- this.in = in;
- this.out = out;
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return an array representation of the Stop
- */
- Object[] toValueObject() {
- return new Object[] {in instanceof CompositeValue ? ((CompositeValue) in).toValueObject() : in, out};
- }
-
- @Override
- public String toString() {
- return String.format("[%s, %s]", in, out);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stops.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stops.java
deleted file mode 100644
index af4f53c072..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/Stops.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.mapbox.mapboxsdk.style.functions.stops;
-
-import android.support.annotation.CallSuper;
-import android.support.annotation.NonNull;
-import android.support.annotation.Size;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * The base class for different stops implementations
- *
- * @param <I> the input type
- * @param <O> the output type
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">The style specification</a>
- */
-public abstract class Stops<I, O> {
-
- /**
- * Convenience method for use in function declarations
- *
- * @param stops the collections of discrete stops
- * @param <I> the Stops input type
- * @param <O> the Stops output type
- * @return the {@link Stops} implementation
- * @see Stop#stop
- * @see CategoricalStops
- */
- @SafeVarargs
- public static <I, O> CategoricalStops<I, O> categorical(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- return new CategoricalStops<>(stops);
- }
-
- /**
- * Convenience method for use in function declarations
- *
- * @param stops the collections of discrete stops
- * @param <I> the Stops input type
- * @param <O> the Stops output type
- * @return the {@link Stops} implementation
- * @see Stop#stop
- * @see ExponentialStops
- */
- @SafeVarargs
- public static <I, O> ExponentialStops<I, O> exponential(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- return new ExponentialStops<>(stops);
- }
-
- /**
- * Convenience method for use in function declarations
- *
- * @param <T> the Stops input/output type
- * @return the {@link IdentityStops} implementation
- * @see Stop#stop
- * @see IdentityStops
- */
- public static <T> IdentityStops<T> identity() {
- return new IdentityStops<>();
- }
-
- /**
- * Convenience method for use in function declarations
- *
- * @param stops the collections of discrete stops
- * @param <I> the Stops input type
- * @param <O> the Stops output type
- * @return the {@link Stops} implementation
- * @see Stop#stop
- * @see IntervalStops
- */
- @SafeVarargs
- public static <I, O> IntervalStops<I, O> interval(@NonNull @Size(min = 1) Stop<I, O>... stops) {
- return new IntervalStops<>(stops);
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return the value object representation for conversion to core
- */
- @CallSuper
- public Map<String, Object> toValueObject() {
- HashMap<String, Object> map = new HashMap<>();
- map.put("type", getTypeName());
- return map;
- }
-
- /**
- * INTERNAL USAGE ONLY
- *
- * @return the unique type name as a string according to the style specification
- */
- protected abstract String getTypeName();
-
- @Override
- public String toString() {
- return getTypeName();
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/package-info.java
deleted file mode 100644
index fa388a9589..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/functions/stops/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * Contains the Mapbox Maps Android Style Function Stop implementation API classes.
- *
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-function">The style specification</a>
- */
-package com.mapbox.mapboxsdk.style.functions.stops;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
index 978fa29ef8..60c2aa907b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
index 10663142b5..94929398d5 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class CircleLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public CircleLayer withFilter(Filter.Statement filter) {
+ public CircleLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
index f77e7280f0..a0ba1e2159 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java
@@ -9,20 +9,8 @@ package com.mapbox.mapboxsdk.style.layers;
public class CustomLayer extends Layer {
public CustomLayer(String id,
- long context,
- long initializeFunction,
- long renderFunction,
- long deinitializeFunction) {
- this(id, context, initializeFunction, renderFunction, 0L, deinitializeFunction);
- }
-
- public CustomLayer(String id,
- long context,
- long initializeFunction,
- long renderFunction,
- long contextLostFunction,
- long deinitializeFunction) {
- initialize(id, initializeFunction, renderFunction, contextLostFunction, deinitializeFunction, context);
+ long host) {
+ initialize(id, host);
}
public CustomLayer(long nativePtr) {
@@ -33,9 +21,7 @@ public class CustomLayer extends Layer {
nativeUpdate();
}
- protected native void initialize(String id, long initializeFunction, long renderFunction,
- long contextLostFunction, long deinitializeFunction,
- long context);
+ protected native void initialize(String id, long host);
protected native void nativeUpdate();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
index 6772da73b1..29e2b49d76 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class FillExtrusionLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public FillExtrusionLayer withFilter(Filter.Statement filter) {
+ public FillExtrusionLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
index 3719ae9e1b..ed780811c0 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class FillLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public FillLayer withFilter(Filter.Statement filter) {
+ public FillLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
deleted file mode 100644
index 4dbb461e4c..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
+++ /dev/null
@@ -1,230 +0,0 @@
-package com.mapbox.mapboxsdk.style.layers;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-/**
- * Utility to build filter expressions more easily.
- *
- * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-filter">The online documentation</a>
- */
-public class Filter {
-
- /**
- * Base Filter statement. Subclassed to provide concrete statements.
- */
- public abstract static class Statement {
- protected final String operator;
-
- public Statement(String operator) {
- this.operator = operator;
- }
-
- /**
- * Generate a raw array representation of the filter
- *
- * @return the filter represented as an array
- */
- public abstract Object[] toArray();
- }
-
- /**
- * Represents a {@link Filter} statement. Can be unary (eg `has()`, etc) or take any number of values.
- */
- private static class SimpleStatement extends Statement {
- private final String key;
- private final Object[] values;
-
- /**
- * @param operator the operator (eg `=`, etc)
- * @param key the property key
- * @param values the values to operate on, if any
- */
- SimpleStatement(String operator, String key, Object... values) {
- super(operator);
- this.key = key;
- this.values = values;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object[] toArray() {
- ArrayList<Object> array = new ArrayList<>(2 + values.length);
- array.add(operator);
- array.add(key);
- Collections.addAll(array, values);
- return array.toArray();
- }
- }
-
- /**
- * Represents a collection of {@link Statement}s with an operator that describes their relationship
- */
- private static class CompoundStatement extends Statement {
- private final Statement[] statements;
-
- /**
- * @param operator the relationship operator
- * @param statements the statements to compound
- */
- CompoundStatement(String operator, Statement... statements) {
- super(operator);
- this.statements = statements;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object[] toArray() {
- ArrayList<Object> array = new ArrayList<>(1 + statements.length);
- array.add(operator);
- for (Statement statement : statements) {
- array.add(statement.toArray());
- }
- return array.toArray();
- }
- }
-
- /**
- * Groups a collection of statements in an 'all' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement all(Statement... statements) {
- return new CompoundStatement("all", statements);
- }
-
- /**
- * Groups a collection of statements in an 'any' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement any(Statement... statements) {
- return new CompoundStatement("any", statements);
- }
-
- /**
- * Groups a collection of statements in an 'none' relationship
- *
- * @param statements the collection of statements
- * @return the statements compounded
- */
- public static Statement none(Statement... statements) {
- return new CompoundStatement("none", statements);
- }
-
- /**
- * Check the property's existence
- *
- * @param key the property key
- * @return the statement
- */
- public static Statement has(String key) {
- return new SimpleStatement("has", key);
- }
-
- /**
- * Check the property's existence, negated
- *
- * @param key the property key
- * @return the statement
- */
- public static Statement notHas(String key) {
- return new SimpleStatement("!has", key);
- }
-
- /**
- * Check the property equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement eq(String key, Object value) {
- return new SimpleStatement("==", key, value);
- }
-
- /**
- * Check the property does not equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement neq(String key, Object value) {
- return new SimpleStatement("!=", key, value);
- }
-
- /**
- * Check the property exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement gt(String key, Object value) {
- return new SimpleStatement(">", key, value);
- }
-
- /**
- * Check the property exceeds or equals the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement gte(String key, Object value) {
- return new SimpleStatement(">=", key, value);
- }
-
- /**
- * Check the property does not exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement lt(String key, Object value) {
- return new SimpleStatement("<", key, value);
- }
-
- /**
- * Check the property equals or does not exceeds the given value
- *
- * @param key the property key
- * @param value the value to check against
- * @return the statement
- */
- public static Statement lte(String key, Object value) {
- return new SimpleStatement("<=", key, value);
- }
-
- /**
- * Check the property is within the given set
- *
- * @param key the property key
- * @param values the set of values to check against
- * @return the statement
- */
- public static Statement in(String key, Object... values) {
- return new SimpleStatement("in", key, values);
- }
-
- /**
- * Check the property is not within the given set
- *
- * @param key the property key
- * @param values the set of values to check against
- * @return the statement
- */
- public static Statement notIn(String key, Object... values) {
- return new SimpleStatement("!in", key, values);
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
new file mode 100644
index 0000000000..42b4210710
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java
@@ -0,0 +1,267 @@
+// 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.Nullable;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * 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 expression filter.
+ *
+ * @param filter the expression filter to set
+ */
+ public void setFilter(Expression filter) {
+ nativeSetFilter(filter.toArray());
+ }
+
+ /**
+ * Set a single expression filter.
+ *
+ * @param filter the expression filter to set
+ * @return This
+ */
+ public HeatmapLayer withFilter(Expression filter) {
+ setFilter(filter);
+ return this;
+ }
+
+ /**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
+ * 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 HeatmapColor property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getHeatmapColor() {
+ return (PropertyValue<String>) new PropertyValue("heatmap-color", nativeGetHeatmapColor());
+ }
+
+ /**
+ * Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `["heatmap-density"]` as input.
+ *
+ * @return int representation of a rgba string color
+ * @throws RuntimeException thrown if property isn't a value
+ */
+ @ColorInt
+ public int getHeatmapColorAsInt() {
+ PropertyValue<String> value = getHeatmapColor();
+ if (value.isValue()) {
+ return rgbaToColor(value.getValue());
+ } else {
+ throw new RuntimeException("heatmap-color was set as a Function");
+ }
+ }
+
+ /**
+ * 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 nativeGetHeatmapColor();
+
+ 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..fb086f424b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java
@@ -0,0 +1,289 @@
+// 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.Nullable;
+import android.support.annotation.UiThread;
+
+import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
+
+/**
+ * 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..411fbe4435 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,7 +2,9 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.NonNull;
-import com.mapbox.mapboxsdk.style.functions.Function;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
/**
* Base class for the different Layer types
@@ -71,6 +73,8 @@ public abstract class Layer {
protected native void nativeSetFilter(Object[] filter);
+ protected native JsonElement nativeGetFilter();
+
protected native void nativeSetSourceLayer(String sourceLayer);
protected native String nativeGetSourceLayer();
@@ -88,6 +92,10 @@ public abstract class Layer {
}
private Object convertValue(Object value) {
- return value != null && value instanceof Function ? ((Function) value).toValueObject() : value;
+ if (value != null && 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/LineLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
index ca106cc106..5e6e6d38e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class LineLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public LineLayer withFilter(Filter.Statement filter) {
+ public LineLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/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..1dd8eddab9 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,9 @@
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 java.util.Locale;
@@ -28,17 +26,6 @@ public class PropertyFactory {
}
/**
- * Set the property visibility.
- *
- * @param <T> the function input type
- * @param function the visibility function
- * @return property wrapper around a String function
- */
- public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
- return new LayoutPropertyValue<>("visibility", function);
- }
-
- /**
* Whether or not the fill should be antialiased.
*
* @param value a Boolean value
@@ -48,16 +35,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-antialias", value);
}
-
/**
* Whether or not the fill should be antialiased.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> fillAntialias(CameraFunction<Z, Boolean> function) {
- return new PaintPropertyValue<>("fill-antialias", function);
+ public static PropertyValue<Expression> fillAntialias(Expression expression) {
+ return new PaintPropertyValue<>("fill-antialias", expression);
}
/**
@@ -70,16 +55,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> fillOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("fill-opacity", function);
+ public static PropertyValue<Expression> fillOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-opacity", expression);
}
/**
@@ -102,16 +85,14 @@ 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 <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> fillColor(Function<T, String> function) {
- return new PaintPropertyValue<>("fill-color", function);
+ public static PropertyValue<Expression> fillColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-color", expression);
}
/**
@@ -134,16 +115,14 @@ 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 <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> fillOutlineColor(Function<T, String> function) {
- return new PaintPropertyValue<>("fill-outline-color", function);
+ public static PropertyValue<Expression> fillOutlineColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-outline-color", expression);
}
/**
@@ -156,20 +135,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("fill-translate", function);
+ public static PropertyValue<Expression> fillTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate", expression);
}
/**
- * 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,20 +155,18 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("fill-translate-anchor", function);
+ public static PropertyValue<Expression> fillTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-translate-anchor", 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
* @param value a String value
* @return property wrapper around String
@@ -200,16 +175,14 @@ 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillPattern(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("fill-pattern", function);
+ public static PropertyValue<Expression> fillPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-pattern", expression);
}
/**
@@ -222,16 +195,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-opacity", value);
}
-
/**
* The opacity at which the line will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> lineOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("line-opacity", function);
+ public static PropertyValue<Expression> lineOpacity(Expression expression) {
+ return new PaintPropertyValue<>("line-opacity", expression);
}
/**
@@ -254,16 +225,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-color", value);
}
-
/**
* The color with which the line will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> lineColor(Function<T, String> function) {
- return new PaintPropertyValue<>("line-color", function);
+ public static PropertyValue<Expression> lineColor(Expression expression) {
+ return new PaintPropertyValue<>("line-color", expression);
}
/**
@@ -276,20 +245,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("line-translate", function);
+ public static PropertyValue<Expression> lineTranslate(Expression expression) {
+ return new PaintPropertyValue<>("line-translate", expression);
}
/**
- * 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,16 +265,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("line-translate-anchor", function);
+ public static PropertyValue<Expression> lineTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("line-translate-anchor", expression);
}
/**
@@ -320,16 +285,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-width", value);
}
-
/**
* Stroke thickness.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> lineWidth(Function<T, Float> function) {
- return new PaintPropertyValue<>("line-width", function);
+ public static PropertyValue<Expression> lineWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-width", expression);
}
/**
@@ -342,16 +305,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> lineGapWidth(Function<T, Float> function) {
- return new PaintPropertyValue<>("line-gap-width", function);
+ public static PropertyValue<Expression> lineGapWidth(Expression expression) {
+ return new PaintPropertyValue<>("line-gap-width", expression);
}
/**
@@ -364,16 +325,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> lineOffset(Function<T, Float> function) {
- return new PaintPropertyValue<>("line-offset", function);
+ public static PropertyValue<Expression> lineOffset(Expression expression) {
+ return new PaintPropertyValue<>("line-offset", expression);
}
/**
@@ -386,20 +345,18 @@ public class PropertyFactory {
return new PaintPropertyValue<>("line-blur", value);
}
-
/**
* Blur applied to the line, in density-independent pixels.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> lineBlur(Function<T, Float> function) {
- return new PaintPropertyValue<>("line-blur", function);
+ public static PropertyValue<Expression> lineBlur(Expression expression) {
+ return new PaintPropertyValue<>("line-blur", 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.
+ * 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. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
* @param value a Float[] value
* @return property wrapper around Float[]
@@ -408,20 +365,18 @@ 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.
+ * 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. Note that GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to the expected scale. Also note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> lineDasharray(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("line-dasharray", function);
+ public static PropertyValue<Expression> lineDasharray(Expression expression) {
+ return new PaintPropertyValue<>("line-dasharray", 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
* @param value a String value
* @return property wrapper around String
@@ -430,16 +385,14 @@ 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> linePattern(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("line-pattern", function);
+ public static PropertyValue<Expression> linePattern(Expression expression) {
+ return new PaintPropertyValue<>("line-pattern", expression);
}
/**
@@ -452,16 +405,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-opacity", value);
}
-
/**
* The opacity at which the icon will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> iconOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("icon-opacity", function);
+ public static PropertyValue<Expression> iconOpacity(Expression expression) {
+ return new PaintPropertyValue<>("icon-opacity", expression);
}
/**
@@ -484,16 +435,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-color", value);
}
-
/**
* The color of the icon. This can only be used with sdf icons.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> iconColor(Function<T, String> function) {
- return new PaintPropertyValue<>("icon-color", function);
+ public static PropertyValue<Expression> iconColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-color", expression);
}
/**
@@ -516,16 +465,14 @@ 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 <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> iconHaloColor(Function<T, String> function) {
- return new PaintPropertyValue<>("icon-halo-color", function);
+ public static PropertyValue<Expression> iconHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-color", expression);
}
/**
@@ -538,16 +485,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-width", value);
}
-
/**
* Distance of halo to the icon outline.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> iconHaloWidth(Function<T, Float> function) {
- return new PaintPropertyValue<>("icon-halo-width", function);
+ public static PropertyValue<Expression> iconHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-width", expression);
}
/**
@@ -560,16 +505,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-halo-blur", value);
}
-
/**
* Fade out the halo towards the outside.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> iconHaloBlur(Function<T, Float> function) {
- return new PaintPropertyValue<>("icon-halo-blur", function);
+ public static PropertyValue<Expression> iconHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("icon-halo-blur", expression);
}
/**
@@ -582,20 +525,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> iconTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("icon-translate", function);
+ public static PropertyValue<Expression> iconTranslate(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate", expression);
}
/**
- * 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,16 +545,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("icon-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("icon-translate-anchor", function);
+ public static PropertyValue<Expression> iconTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("icon-translate-anchor", expression);
}
/**
@@ -626,16 +565,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-opacity", value);
}
-
/**
* The opacity at which the text will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> textOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("text-opacity", function);
+ public static PropertyValue<Expression> textOpacity(Expression expression) {
+ return new PaintPropertyValue<>("text-opacity", expression);
}
/**
@@ -658,16 +595,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-color", value);
}
-
/**
* The color with which the text will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> textColor(Function<T, String> function) {
- return new PaintPropertyValue<>("text-color", function);
+ public static PropertyValue<Expression> textColor(Expression expression) {
+ return new PaintPropertyValue<>("text-color", expression);
}
/**
@@ -690,16 +625,14 @@ 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 <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> textHaloColor(Function<T, String> function) {
- return new PaintPropertyValue<>("text-halo-color", function);
+ public static PropertyValue<Expression> textHaloColor(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-color", expression);
}
/**
@@ -712,16 +645,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> textHaloWidth(Function<T, Float> function) {
- return new PaintPropertyValue<>("text-halo-width", function);
+ public static PropertyValue<Expression> textHaloWidth(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-width", expression);
}
/**
@@ -734,16 +665,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-halo-blur", value);
}
-
/**
* The halo's fadeout distance towards the outside.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> textHaloBlur(Function<T, Float> function) {
- return new PaintPropertyValue<>("text-halo-blur", function);
+ public static PropertyValue<Expression> textHaloBlur(Expression expression) {
+ return new PaintPropertyValue<>("text-halo-blur", expression);
}
/**
@@ -756,20 +685,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> textTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("text-translate", function);
+ public static PropertyValue<Expression> textTranslate(Expression expression) {
+ return new PaintPropertyValue<>("text-translate", expression);
}
/**
- * 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,16 +705,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("text-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("text-translate-anchor", function);
+ public static PropertyValue<Expression> textTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("text-translate-anchor", expression);
}
/**
@@ -800,16 +725,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-radius", value);
}
-
/**
* Circle radius.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> circleRadius(Function<T, Float> function) {
- return new PaintPropertyValue<>("circle-radius", function);
+ public static PropertyValue<Expression> circleRadius(Expression expression) {
+ return new PaintPropertyValue<>("circle-radius", expression);
}
/**
@@ -832,16 +755,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-color", value);
}
-
/**
* The fill color of the circle.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> circleColor(Function<T, String> function) {
- return new PaintPropertyValue<>("circle-color", function);
+ public static PropertyValue<Expression> circleColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-color", expression);
}
/**
@@ -854,16 +775,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> circleBlur(Function<T, Float> function) {
- return new PaintPropertyValue<>("circle-blur", function);
+ public static PropertyValue<Expression> circleBlur(Expression expression) {
+ return new PaintPropertyValue<>("circle-blur", expression);
}
/**
@@ -876,16 +795,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-opacity", value);
}
-
/**
* The opacity at which the circle will be drawn.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> circleOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("circle-opacity", function);
+ public static PropertyValue<Expression> circleOpacity(Expression expression) {
+ return new PaintPropertyValue<>("circle-opacity", expression);
}
/**
@@ -898,20 +815,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> circleTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("circle-translate", function);
+ public static PropertyValue<Expression> circleTranslate(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate", expression);
}
/**
- * 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,16 +835,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circleTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("circle-translate-anchor", function);
+ public static PropertyValue<Expression> circleTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("circle-translate-anchor", expression);
}
/**
@@ -942,16 +855,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-pitch-scale", value);
}
-
/**
* Controls the scaling behavior of the circle when the map is pitched.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchScale(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("circle-pitch-scale", function);
+ public static PropertyValue<Expression> circlePitchScale(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-scale", expression);
}
/**
@@ -964,16 +875,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-pitch-alignment", value);
}
-
/**
* Orientation of circle when map is pitched.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> circlePitchAlignment(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("circle-pitch-alignment", function);
+ public static PropertyValue<Expression> circlePitchAlignment(Expression expression) {
+ return new PaintPropertyValue<>("circle-pitch-alignment", expression);
}
/**
@@ -986,16 +895,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> circleStrokeWidth(Function<T, Float> function) {
- return new PaintPropertyValue<>("circle-stroke-width", function);
+ public static PropertyValue<Expression> circleStrokeWidth(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-width", expression);
}
/**
@@ -1018,16 +925,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-color", value);
}
-
/**
* The stroke color of the circle.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> circleStrokeColor(Function<T, String> function) {
- return new PaintPropertyValue<>("circle-stroke-color", function);
+ public static PropertyValue<Expression> circleStrokeColor(Expression expression) {
+ return new PaintPropertyValue<>("circle-stroke-color", expression);
}
/**
@@ -1040,16 +945,124 @@ public class PropertyFactory {
return new PaintPropertyValue<>("circle-stroke-opacity", value);
}
-
/**
* The opacity of the circle's stroke.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `["heatmap-density"]` as input.
+ *
+ * @param value a int color value
+ * @return property wrapper around String color
+ */
+ public static PropertyValue<String> heatmapColor(@ColorInt int value) {
+ return new PaintPropertyValue<>("heatmap-color", colorToRgbaString(value));
+ }
+
+ /**
+ * Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `["heatmap-density"]` as input.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> heatmapColor(String value) {
+ return new PaintPropertyValue<>("heatmap-color", value);
+ }
+
+ /**
+ * Defines the color of each pixel based on its density value in a heatmap. Should be an expression that uses `["heatmap-density"]` as input.
+ *
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
+ */
+ public static PropertyValue<Expression> heatmapColor(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-color", expression);
+ }
+
+ /**
+ * 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 <T> PropertyValue<Function<T, Float>> circleStrokeOpacity(Function<T, Float> function) {
- return new PaintPropertyValue<>("circle-stroke-opacity", function);
+ public static PropertyValue<Expression> heatmapOpacity(Expression expression) {
+ return new PaintPropertyValue<>("heatmap-opacity", expression);
}
/**
@@ -1062,16 +1075,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> fillExtrusionOpacity(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("fill-extrusion-opacity", function);
+ public static PropertyValue<Expression> fillExtrusionOpacity(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-opacity", expression);
}
/**
@@ -1094,16 +1105,14 @@ 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 <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, String>> fillExtrusionColor(Function<T, String> function) {
- return new PaintPropertyValue<>("fill-extrusion-color", function);
+ public static PropertyValue<Expression> fillExtrusionColor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-color", expression);
}
/**
@@ -1116,20 +1125,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> fillExtrusionTranslate(CameraFunction<Z, Float[]> function) {
- return new PaintPropertyValue<>("fill-extrusion-translate", function);
+ public static PropertyValue<Expression> fillExtrusionTranslate(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate", expression);
}
/**
- * 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,20 +1145,18 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-translate-anchor", value);
}
-
/**
- * 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
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionTranslateAnchor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("fill-extrusion-translate-anchor", function);
+ public static PropertyValue<Expression> fillExtrusionTranslateAnchor(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-translate-anchor", 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
* @param value a String value
* @return property wrapper around String
@@ -1160,16 +1165,14 @@ 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> fillExtrusionPattern(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("fill-extrusion-pattern", function);
+ public static PropertyValue<Expression> fillExtrusionPattern(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-pattern", expression);
}
/**
@@ -1182,16 +1185,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("fill-extrusion-height", value);
}
-
/**
* The height with which to extrude this layer.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> fillExtrusionHeight(Function<T, Float> function) {
- return new PaintPropertyValue<>("fill-extrusion-height", function);
+ public static PropertyValue<Expression> fillExtrusionHeight(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-height", expression);
}
/**
@@ -1204,16 +1205,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <T> PropertyValue<Function<T, Float>> fillExtrusionBase(Function<T, Float> function) {
- return new PaintPropertyValue<>("fill-extrusion-base", function);
+ public static PropertyValue<Expression> fillExtrusionBase(Expression expression) {
+ return new PaintPropertyValue<>("fill-extrusion-base", expression);
}
/**
@@ -1226,16 +1225,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-opacity", value);
}
-
/**
* The opacity at which the image will be drawn.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterOpacity(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-opacity", function);
+ public static PropertyValue<Expression> rasterOpacity(Expression expression) {
+ return new PaintPropertyValue<>("raster-opacity", expression);
}
/**
@@ -1248,16 +1245,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-hue-rotate", value);
}
-
/**
* Rotates hues around the color wheel.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterHueRotate(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-hue-rotate", function);
+ public static PropertyValue<Expression> rasterHueRotate(Expression expression) {
+ return new PaintPropertyValue<>("raster-hue-rotate", expression);
}
/**
@@ -1270,16 +1265,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMin(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-brightness-min", function);
+ public static PropertyValue<Expression> rasterBrightnessMin(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-min", expression);
}
/**
@@ -1292,16 +1285,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterBrightnessMax(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-brightness-max", function);
+ public static PropertyValue<Expression> rasterBrightnessMax(Expression expression) {
+ return new PaintPropertyValue<>("raster-brightness-max", expression);
}
/**
@@ -1314,16 +1305,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-saturation", value);
}
-
/**
* Increase or reduce the saturation of the image.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterSaturation(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-saturation", function);
+ public static PropertyValue<Expression> rasterSaturation(Expression expression) {
+ return new PaintPropertyValue<>("raster-saturation", expression);
}
/**
@@ -1336,16 +1325,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-contrast", value);
}
-
/**
* Increase or reduce the contrast of the image.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterContrast(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-contrast", function);
+ public static PropertyValue<Expression> rasterContrast(Expression expression) {
+ return new PaintPropertyValue<>("raster-contrast", expression);
}
/**
@@ -1358,16 +1345,164 @@ public class PropertyFactory {
return new PaintPropertyValue<>("raster-fade-duration", value);
}
-
/**
* Fade duration when a new tile is added.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 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 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 <Z extends Number> PropertyValue<CameraFunction<Z, Float>> rasterFadeDuration(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("raster-fade-duration", function);
+ public static PropertyValue<Expression> hillshadeAccentColor(Expression expression) {
+ return new PaintPropertyValue<>("hillshade-accent-color", expression);
}
/**
@@ -1390,20 +1525,18 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-color", value);
}
-
/**
* The color with which the background will be drawn.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundColor(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("background-color", function);
+ public static PropertyValue<Expression> backgroundColor(Expression expression) {
+ return new PaintPropertyValue<>("background-color", 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
* @param value a String value
* @return property wrapper around String
@@ -1412,16 +1545,14 @@ 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).
+ * 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). Note that zoom-dependent expressions will be evaluated only at integer zoom levels.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> backgroundPattern(CameraFunction<Z, String> function) {
- return new PaintPropertyValue<>("background-pattern", function);
+ public static PropertyValue<Expression> backgroundPattern(Expression expression) {
+ return new PaintPropertyValue<>("background-pattern", expression);
}
/**
@@ -1434,16 +1565,14 @@ public class PropertyFactory {
return new PaintPropertyValue<>("background-opacity", value);
}
-
/**
* The opacity at which the background will be drawn.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> backgroundOpacity(CameraFunction<Z, Float> function) {
- return new PaintPropertyValue<>("background-opacity", function);
+ public static PropertyValue<Expression> backgroundOpacity(Expression expression) {
+ return new PaintPropertyValue<>("background-opacity", expression);
}
/**
@@ -1456,17 +1585,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-cap", value);
}
-
-
/**
* The display of line endings.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> lineCap(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("line-cap", function);
+ public static PropertyValue<Expression> lineCap(Expression value) {
+ return new LayoutPropertyValue<>("line-cap", value);
}
/**
@@ -1479,17 +1605,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-join", value);
}
-
-
/**
* The display of lines when joining.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> lineJoin(Function<T, String> function) {
- return new LayoutPropertyValue<>("line-join", function);
+ public static PropertyValue<Expression> lineJoin(Expression value) {
+ return new LayoutPropertyValue<>("line-join", value);
}
/**
@@ -1502,17 +1625,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-miter-limit", value);
}
-
-
/**
* Used to automatically convert miter joins to bevel joins for sharp angles.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> lineMiterLimit(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("line-miter-limit", function);
+ public static PropertyValue<Expression> lineMiterLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-miter-limit", value);
}
/**
@@ -1525,17 +1645,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("line-round-limit", value);
}
-
-
/**
* Used to automatically convert round joins to miter joins for shallow angles.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> lineRoundLimit(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("line-round-limit", function);
+ public static PropertyValue<Expression> lineRoundLimit(Expression value) {
+ return new LayoutPropertyValue<>("line-round-limit", value);
}
/**
@@ -1548,17 +1665,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-placement", value);
}
-
-
/**
* Label placement relative to its geometry.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> symbolPlacement(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("symbol-placement", function);
+ public static PropertyValue<Expression> symbolPlacement(Expression value) {
+ return new LayoutPropertyValue<>("symbol-placement", value);
}
/**
@@ -1571,17 +1685,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("symbol-spacing", value);
}
-
-
/**
* Distance between two symbol anchors.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> symbolSpacing(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("symbol-spacing", function);
+ public static PropertyValue<Expression> symbolSpacing(Expression value) {
+ return new LayoutPropertyValue<>("symbol-spacing", value);
}
/**
@@ -1594,17 +1705,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> symbolAvoidEdges(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("symbol-avoid-edges", function);
+ public static PropertyValue<Expression> symbolAvoidEdges(Expression value) {
+ return new LayoutPropertyValue<>("symbol-avoid-edges", value);
}
/**
@@ -1617,17 +1725,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> iconAllowOverlap(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("icon-allow-overlap", function);
+ public static PropertyValue<Expression> iconAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("icon-allow-overlap", value);
}
/**
@@ -1640,17 +1745,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> iconIgnorePlacement(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("icon-ignore-placement", function);
+ public static PropertyValue<Expression> iconIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("icon-ignore-placement", value);
}
/**
@@ -1663,17 +1765,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> iconOptional(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("icon-optional", function);
+ public static PropertyValue<Expression> iconOptional(Expression value) {
+ return new LayoutPropertyValue<>("icon-optional", value);
}
/**
@@ -1686,17 +1785,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotation-alignment", value);
}
-
-
/**
* In combination with {@link Property.SYMBOL_PLACEMENT}, determines the rotation behavior of icons.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconRotationAlignment(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("icon-rotation-alignment", function);
+ public static PropertyValue<Expression> iconRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotation-alignment", value);
}
/**
@@ -1709,17 +1805,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> iconSize(Function<T, Float> function) {
- return new LayoutPropertyValue<>("icon-size", function);
+ public static PropertyValue<Expression> iconSize(Expression value) {
+ return new LayoutPropertyValue<>("icon-size", value);
}
/**
@@ -1732,17 +1825,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-text-fit", value);
}
-
-
/**
* Scales the icon to fit around the associated text.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconTextFit(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("icon-text-fit", function);
+ public static PropertyValue<Expression> iconTextFit(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit", value);
}
/**
@@ -1755,21 +1845,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float[]
- * @return property wrapper around a Float[] function
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float[]>> iconTextFitPadding(CameraFunction<Z, Float[]> function) {
- return new LayoutPropertyValue<>("icon-text-fit-padding", function);
+ public static PropertyValue<Expression> iconTextFitPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-text-fit-padding", 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.
*
* @param value a String value
* @return property wrapper around String
@@ -1778,17 +1865,14 @@ public class PropertyFactory {
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.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> iconImage(Function<T, String> function) {
- return new LayoutPropertyValue<>("icon-image", function);
+ public static PropertyValue<Expression> iconImage(Expression value) {
+ return new LayoutPropertyValue<>("icon-image", value);
}
/**
@@ -1801,17 +1885,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-rotate", value);
}
-
-
/**
* Rotates the icon clockwise.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> iconRotate(Function<T, Float> function) {
- return new LayoutPropertyValue<>("icon-rotate", function);
+ public static PropertyValue<Expression> iconRotate(Expression value) {
+ return new LayoutPropertyValue<>("icon-rotate", value);
}
/**
@@ -1824,17 +1905,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> iconPadding(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("icon-padding", function);
+ public static PropertyValue<Expression> iconPadding(Expression value) {
+ return new LayoutPropertyValue<>("icon-padding", value);
}
/**
@@ -1847,21 +1925,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> iconKeepUpright(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("icon-keep-upright", function);
+ public static PropertyValue<Expression> iconKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("icon-keep-upright", 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 value a Float[] value
* @return property wrapper around Float[]
@@ -1870,17 +1945,14 @@ 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. 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[]
- * @return property wrapper around a Float[] function
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
*/
- public static <T> PropertyValue<Function<T, Float[]>> iconOffset(Function<T, Float[]> function) {
- return new LayoutPropertyValue<>("icon-offset", function);
+ public static PropertyValue<Expression> iconOffset(Expression value) {
+ return new LayoutPropertyValue<>("icon-offset", value);
}
/**
@@ -1893,17 +1965,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-anchor", value);
}
-
-
/**
* Part of the icon placed closest to the anchor.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> iconAnchor(Function<T, String> function) {
- return new LayoutPropertyValue<>("icon-anchor", function);
+ public static PropertyValue<Expression> iconAnchor(Expression value) {
+ return new LayoutPropertyValue<>("icon-anchor", value);
}
/**
@@ -1916,17 +1985,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("icon-pitch-alignment", value);
}
-
-
/**
* Orientation of icon when map is pitched.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconPitchAlignment(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("icon-pitch-alignment", function);
+ public static PropertyValue<Expression> iconPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", value);
}
/**
@@ -1939,17 +2005,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-pitch-alignment", value);
}
-
-
/**
* Orientation of text when map is pitched.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textPitchAlignment(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("text-pitch-alignment", function);
+ public static PropertyValue<Expression> textPitchAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-pitch-alignment", value);
}
/**
@@ -1962,21 +2025,18 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> textRotationAlignment(CameraFunction<Z, String> function) {
- return new LayoutPropertyValue<>("text-rotation-alignment", function);
+ public static PropertyValue<Expression> textRotationAlignment(Expression value) {
+ return new LayoutPropertyValue<>("text-rotation-alignment", 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.
*
* @param value a String value
* @return property wrapper around String
@@ -1985,17 +2045,14 @@ public class PropertyFactory {
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.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> textField(Function<T, String> function) {
- return new LayoutPropertyValue<>("text-field", function);
+ public static PropertyValue<Expression> textField(Expression value) {
+ return new LayoutPropertyValue<>("text-field", value);
}
/**
@@ -2008,17 +2065,14 @@ public class PropertyFactory {
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[]
- * @return property wrapper around a String[] function
+ * @param value a String[] value
+ * @return property wrapper around String[]
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String[]>> textFont(CameraFunction<Z, String[]> function) {
- return new LayoutPropertyValue<>("text-font", function);
+ public static PropertyValue<Expression> textFont(Expression value) {
+ return new LayoutPropertyValue<>("text-font", value);
}
/**
@@ -2031,17 +2085,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-size", value);
}
-
-
/**
* Font size.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> textSize(Function<T, Float> function) {
- return new LayoutPropertyValue<>("text-size", function);
+ public static PropertyValue<Expression> textSize(Expression value) {
+ return new LayoutPropertyValue<>("text-size", value);
}
/**
@@ -2054,17 +2105,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-width", value);
}
-
-
/**
* The maximum line width for text wrapping.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> textMaxWidth(Function<T, Float> function) {
- return new LayoutPropertyValue<>("text-max-width", function);
+ public static PropertyValue<Expression> textMaxWidth(Expression value) {
+ return new LayoutPropertyValue<>("text-max-width", value);
}
/**
@@ -2077,17 +2125,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-line-height", value);
}
-
-
/**
* Text leading value for multi-line text.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textLineHeight(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("text-line-height", function);
+ public static PropertyValue<Expression> textLineHeight(Expression value) {
+ return new LayoutPropertyValue<>("text-line-height", value);
}
/**
@@ -2100,17 +2145,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-letter-spacing", value);
}
-
-
/**
* Text tracking amount.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> textLetterSpacing(Function<T, Float> function) {
- return new LayoutPropertyValue<>("text-letter-spacing", function);
+ public static PropertyValue<Expression> textLetterSpacing(Expression value) {
+ return new LayoutPropertyValue<>("text-letter-spacing", value);
}
/**
@@ -2123,17 +2165,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-justify", value);
}
-
-
/**
* Text justification options.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> textJustify(Function<T, String> function) {
- return new LayoutPropertyValue<>("text-justify", function);
+ public static PropertyValue<Expression> textJustify(Expression value) {
+ return new LayoutPropertyValue<>("text-justify", value);
}
/**
@@ -2146,17 +2185,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-anchor", value);
}
-
-
/**
* Part of the text placed closest to the anchor.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> textAnchor(Function<T, String> function) {
- return new LayoutPropertyValue<>("text-anchor", function);
+ public static PropertyValue<Expression> textAnchor(Expression value) {
+ return new LayoutPropertyValue<>("text-anchor", value);
}
/**
@@ -2169,17 +2205,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-max-angle", value);
}
-
-
/**
* Maximum angle change between adjacent characters.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textMaxAngle(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("text-max-angle", function);
+ public static PropertyValue<Expression> textMaxAngle(Expression value) {
+ return new LayoutPropertyValue<>("text-max-angle", value);
}
/**
@@ -2192,17 +2225,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-rotate", value);
}
-
-
/**
* Rotates the text clockwise.
*
- * @param <T> the function input type
- * @param function a wrapper function for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <T> PropertyValue<Function<T, Float>> textRotate(Function<T, Float> function) {
- return new LayoutPropertyValue<>("text-rotate", function);
+ public static PropertyValue<Expression> textRotate(Expression value) {
+ return new LayoutPropertyValue<>("text-rotate", value);
}
/**
@@ -2215,17 +2245,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Float
- * @return property wrapper around a Float function
+ * @param value a Float value
+ * @return property wrapper around Float
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Float>> textPadding(CameraFunction<Z, Float> function) {
- return new LayoutPropertyValue<>("text-padding", function);
+ public static PropertyValue<Expression> textPadding(Expression value) {
+ return new LayoutPropertyValue<>("text-padding", value);
}
/**
@@ -2238,17 +2265,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> textKeepUpright(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("text-keep-upright", function);
+ public static PropertyValue<Expression> textKeepUpright(Expression value) {
+ return new LayoutPropertyValue<>("text-keep-upright", value);
}
/**
@@ -2261,17 +2285,14 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("text-transform", value);
}
-
-
/**
* Specifies how to capitalize text, similar to the CSS {@link PropertyFactory#textTransform} property.
*
- * @param <T> the function input type
- * @param function a wrapper function for String
- * @return property wrapper around a String function
+ * @param value a String value
+ * @return property wrapper around String
*/
- public static <T> PropertyValue<Function<T, String>> textTransform(Function<T, String> function) {
- return new LayoutPropertyValue<>("text-transform", function);
+ public static PropertyValue<Expression> textTransform(Expression value) {
+ return new LayoutPropertyValue<>("text-transform", value);
}
/**
@@ -2284,17 +2305,14 @@ 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 <T> the function input type
- * @param function a wrapper function for Float[]
- * @return property wrapper around a Float[] function
+ * @param value a Float[] value
+ * @return property wrapper around Float[]
*/
- public static <T> PropertyValue<Function<T, Float[]>> textOffset(Function<T, Float[]> function) {
- return new LayoutPropertyValue<>("text-offset", function);
+ public static PropertyValue<Expression> textOffset(Expression value) {
+ return new LayoutPropertyValue<>("text-offset", value);
}
/**
@@ -2307,17 +2325,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> textAllowOverlap(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("text-allow-overlap", function);
+ public static PropertyValue<Expression> textAllowOverlap(Expression value) {
+ return new LayoutPropertyValue<>("text-allow-overlap", value);
}
/**
@@ -2330,17 +2345,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> textIgnorePlacement(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("text-ignore-placement", function);
+ public static PropertyValue<Expression> textIgnorePlacement(Expression value) {
+ return new LayoutPropertyValue<>("text-ignore-placement", value);
}
/**
@@ -2353,16 +2365,14 @@ 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 <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for Boolean
- * @return property wrapper around a Boolean function
+ * @param value a Boolean value
+ * @return property wrapper around Boolean
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, Boolean>> textOptional(CameraFunction<Z, Boolean> function) {
- return new LayoutPropertyValue<>("text-optional", function);
+ public static PropertyValue<Expression> textOptional(Expression value) {
+ return new LayoutPropertyValue<>("text-optional", value);
}
public static String colorToRgbaString(@ColorInt int value) {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
index a57c440df4..fa1779a6c7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java
@@ -4,8 +4,9 @@ import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.google.gson.JsonArray;
import com.mapbox.mapboxsdk.exceptions.ConversionException;
-import com.mapbox.mapboxsdk.style.functions.Function;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.utils.ColorUtils;
import timber.log.Timber;
@@ -40,35 +41,39 @@ public class PropertyValue<T> {
}
/**
- * Returns if this is a function.
+ * Returns if this is a expression.
*
- * @return true if is a function, false if not
+ * @return true if this is a expression, false if not
*/
- public boolean isFunction() {
- return !isNull() && value instanceof Function;
+ public boolean isExpression() {
+ return !isNull() && value instanceof JsonArray;
}
/**
- * Returns if this is a value.
+ * Get the expression of the property.
*
- * @return true if is a value, false if not
+ * @return the property expression
*/
- public boolean isValue() {
- return !isNull() && !isFunction();
- }
-
@Nullable
- public Function<?, T> getFunction() {
- if (isFunction()) {
- // noinspection unchecked
- return (Function<?, T>) value;
+ public Expression getExpression() {
+ if (isExpression()) {
+ return Expression.Converter.convert((JsonArray) value);
} else {
- Timber.w("not a function, try value");
+ Timber.w("not a expression, try value");
return null;
}
}
/**
+ * Returns if this is a value.
+ *
+ * @return true if is a value, false if not
+ */
+ public boolean isValue() {
+ return !isNull() && !isExpression();
+ }
+
+ /**
* Get the value of the property.
*
* @return the property value
@@ -77,7 +82,7 @@ public class PropertyValue<T> {
public T getValue() {
if (isValue()) {
// noinspection unchecked
- return (T) value;
+ return value;
} else {
Timber.w("not a value, try function");
return null;
@@ -114,4 +119,4 @@ public class PropertyValue<T> {
public String toString() {
return String.format("%s: %s", name, value);
}
-}
+} \ No newline at end of file
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..0c7948f62a 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -250,24 +253,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 +291,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/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
index d0fb82dce5..6a2e131d7d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
@@ -4,10 +4,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -69,26 +72,41 @@ public class SymbolLayer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public SymbolLayer withFilter(Filter.Statement filter) {
+ public SymbolLayer withFilter(Expression filter) {
setFilter(filter);
return this;
}
/**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
+ /**
* Set a property or properties.
*
* @param properties the var-args properties
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
index 56e0af8b45..851a85f3d6 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs
@@ -9,10 +9,13 @@ package com.mapbox.mapboxsdk.style.layers;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import static com.mapbox.mapboxsdk.utils.ColorUtils.rgbaToColor;
+import com.google.gson.JsonArray;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
/**
@@ -78,7 +81,7 @@ public class <%- camelize(type) %>Layer extends Layer {
}
<% } -%>
-<% if (type !== 'background' && type !== 'raster') { -%>
+<% if (type !== 'background' && type !== 'raster' && type !== 'hillshade') { -%>
/**
* Get the source layer.
*
@@ -89,25 +92,40 @@ public class <%- camelize(type) %>Layer extends Layer {
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
*/
- public void setFilter(Filter.Statement filter) {
+ public void setFilter(Expression filter) {
nativeSetFilter(filter.toArray());
}
/**
- * Set a single filter.
+ * Set a single expression filter.
*
- * @param filter the filter to set
+ * @param filter the expression filter to set
* @return This
*/
- public <%- camelize(type) %>Layer withFilter(Filter.Statement filter) {
+ public <%- camelize(type) %>Layer withFilter(Expression filter) {
setFilter(filter);
return this;
}
+ /**
+ * Get a single expression filter.
+ *
+ * @return the expression filter to get
+ */
+ @Nullable
+ public Expression getFilter() {
+ Expression expression = null;
+ JsonArray array = (JsonArray) nativeGetFilter();
+ if (array != null) {
+ expression = Expression.Converter.convert(array);
+ }
+ return expression;
+ }
+
<% } -%>
/**
* Set a property or properties.
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs
index 2d3421d1d9..6480dde3c8 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,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 java.util.Locale;
/**
* Constructs paint/layout properties for Layers
@@ -29,17 +29,6 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("visibility", value);
}
- /**
- * Set the property visibility.
- *
- * @param <T> the function input type
- * @param function the visibility function
- * @return property wrapper around a String function
- */
- public static <T> PropertyValue<Function<T, String>> visibility(Function<T, String> function) {
- return new LayoutPropertyValue<>("visibility", function);
- }
-
<% for (const property of paintProperties) { -%>
<% if (property.type == 'color') { -%>
/**
@@ -63,34 +52,17 @@ public class PropertyFactory {
return new PaintPropertyValue<>("<%- property.name %>", value);
}
-<% if (supportsPropertyFunction(property)) { -%>
-
- /**
- * <%- propertyFactoryMethodDoc(property) %>
- *
- * @param <T> the function input type
- * @param function a wrapper function for <%- propertyType(property) %>
- * @return property wrapper around a <%- propertyType(property) %> function
- */
- public static <T> PropertyValue<Function<T, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<T, <%- propertyType(property) %>> function) {
- return new PaintPropertyValue<>("<%- property.name %>", function);
- }
-
-<% } else if (supportsZoomFunction(property)) { -%>
-
/**
* <%- propertyFactoryMethodDoc(property) %>
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for <%- propertyType(property) %>
- * @return property wrapper around a <%- propertyType(property) %> function
+ * @param expression an expression statement
+ * @return property wrapper around an expression statement
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(CameraFunction<Z, <%- propertyType(property) %>> function) {
- return new PaintPropertyValue<>("<%- property.name %>", function);
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression expression) {
+ return new PaintPropertyValue<>("<%- property.name %>", expression);
}
<% } -%>
-<% } -%>
<% for (const property of layoutProperties) { -%>
/**
* <%- propertyFactoryMethodDoc(property) %>
@@ -102,38 +74,20 @@ public class PropertyFactory {
return new LayoutPropertyValue<>("<%- property.name %>", value);
}
-
-<% if (supportsPropertyFunction(property)) { -%>
-
- /**
- * <%- propertyFactoryMethodDoc(property) %>
- *
- * @param <T> the function input type
- * @param function a wrapper function for <%- propertyType(property) %>
- * @return property wrapper around a <%- propertyType(property) %> function
- */
- public static <T> PropertyValue<Function<T, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(Function<T, <%- propertyType(property) %>> function) {
- return new LayoutPropertyValue<>("<%- property.name %>", function);
- }
-
-<% } else if (supportsZoomFunction(property)) { -%>
-
/**
* <%- propertyFactoryMethodDoc(property) %>
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for <%- propertyType(property) %>
- * @return property wrapper around a <%- propertyType(property) %> function
+ * @param value a <%- propertyType(property) %> value
+ * @return property wrapper around <%- propertyType(property) %>
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, <%- propertyType(property) %>>> <%- camelizeWithLeadingLowercase(property.name) %>(CameraFunction<Z, <%- propertyType(property) %>> function) {
- return new LayoutPropertyValue<>("<%- property.name %>", function);
+ public static PropertyValue<Expression> <%- camelizeWithLeadingLowercase(property.name) %>(Expression value) {
+ return new LayoutPropertyValue<>("<%- property.name %>", value);
}
<% } -%>
-<% } -%>
- @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/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java
index 8f23e7d01e..7df48001cc 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java
@@ -48,7 +48,7 @@ public class Light {
}
/**
- * Set the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below).
+ * Set the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0&#xB0; (0&#xB0; when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0&#xB0; when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0&#xB0;, directly above, to 180&#xB0;, directly below).
*
* @param position of the light
*/
@@ -57,7 +57,7 @@ public class Light {
}
/**
- * Get the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0° (0° when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0° when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0°, directly above, to 180°, directly below).
+ * Get the Position property. Position of the light source relative to lit (extruded) geometries, in [r radial coordinate, a azimuthal angle, p polar angle] where r indicates the distance from the center of the base of an object to its light, a indicates the position of the light relative to 0&#xB0; (0&#xB0; when `light.anchor` is set to `viewport` corresponds to the top of the viewport, or 0&#xB0; when `light.anchor` is set to `map` corresponds to due north, and degrees proceed clockwise), and p indicates the height of the light (from 0&#xB0;, directly above, to 180&#xB0;, directly below).
*
* @return position as Position
*/
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java
index cd6218d3e2..00f8486a1c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java
@@ -5,11 +5,11 @@ package com.mapbox.mapboxsdk.style.light;
* <p>
* The position is constructed out of a radial coordinate, an azimuthal angle and a polar angle.
* where the radial coordinate indicates the distance from the center of the base of an object to its light, the
- * azimuthal angle indicates the position of the light relative to 0° (0° when
+ * azimuthal angle indicates the position of the light relative to 0&#xB0; (0&#xB0; when
* {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to viewport corresponds to the top of the
- * viewport, or 0° when {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to map corresponds to due
+ * viewport, or 0&#xB0; when {@link com.mapbox.mapboxsdk.style.layers.Property.ANCHOR} is set to map corresponds to due
* north, and degrees proceed clockwise), and polar indicates the height of the light
- * (from 0°, directly above, to 180°, directly below).
+ * (from 0&#xB0;, directly above, to 180&#xB0;, directly below).
*/
public class Position {
@@ -21,7 +21,7 @@ public class Position {
* Creates a Position from a radial coordinate, an azimuthal angle and a polar angle.
*
* @param radialCoordinate the distance from the center of the base of an object to its light
- * @param azimuthalAngle the position of the light relative to 0°
+ * @param azimuthalAngle the position of the light relative to 0&#xB0;
* @param polarAngle the height of the light
*/
public Position(float radialCoordinate, float azimuthalAngle, float polarAngle) {
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..21a34f6064
--- /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.expressions.Expression;
+
+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 expression to filter the returned Features
+ * @return the features
+ */
+ @NonNull
+ public List<Feature> querySourceFeatures(@Nullable Expression 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..efacc18741 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
@@ -4,10 +4,10 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
-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 com.mapbox.geojson.Feature;
+import com.mapbox.geojson.FeatureCollection;
+import com.mapbox.geojson.Geometry;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import java.net.URL;
import java.util.ArrayList;
@@ -187,7 +187,7 @@ public class GeoJsonSource extends Source {
*
* @param geometry the GeoJSON {@link Geometry} to set
*/
- public void setGeoJson(Geometry<?> geometry) {
+ public void setGeoJson(Geometry geometry) {
nativeSetGeometry(geometry);
}
@@ -238,11 +238,11 @@ public class GeoJsonSource extends Source {
/**
* Queries the source for features.
*
- * @param filter an optional filter statement to filter the returned Features
+ * @param filter an optional filter expression to filter the returned Features
* @return the features
*/
@NonNull
- public List<Feature> querySourceFeatures(@Nullable Filter.Statement filter) {
+ public List<Feature> querySourceFeatures(@Nullable Expression filter) {
Feature[] features = querySourceFeatures(filter != null ? filter.toArray() : null);
return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
}
@@ -259,7 +259,7 @@ public class GeoJsonSource extends Source {
private native void nativeSetFeature(Feature feature);
- private native void nativeSetGeometry(Geometry<?> geometry);
+ private native void nativeSetGeometry(Geometry geometry);
private native Feature[] querySourceFeatures(Object[] filter);
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/TileSet.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java
index 54e4e5f5d3..25df2d91e7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java
@@ -9,6 +9,7 @@ import java.util.Map;
/**
* Tile set, allows using TileJson specification as source.
+ * Note that `encoding` is only relevant to `raster-dem` sources, and is not supported in the TileJson spec.
*
* @see <a href="https://github.com/mapbox/tilejson-spec/tree/master/2.1.0">The tileset specification</a>
*/
@@ -28,6 +29,7 @@ public class TileSet {
private Float maxZoom;
private Float[] bounds;
private Float[] center;
+ private String encoding;
/**
* @param tilejson A semver.org style version number. Describes the version of the TileJSON spec that is implemented
@@ -246,6 +248,20 @@ public class TileSet {
this.bounds = bounds;
}
+ public String getEncoding() {
+ return encoding;
+ }
+
+ /**
+ * Default: "mapbox". The encoding formula for a raster-dem tileset.
+ * Supported values are "mapbox" and "terrarium".
+ *
+ * @param encoding the String encoding formula to set
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
public Float[] getCenter() {
return center;
}
@@ -313,6 +329,10 @@ public class TileSet {
if (center != null) {
result.put("center", center);
}
+ if (encoding != null) {
+ result.put("encoding", encoding);
+ }
+
return result;
}
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..d82eaaa1c7 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
@@ -5,8 +5,8 @@ import android.support.annotation.Nullable;
import android.support.annotation.Size;
import android.support.annotation.UiThread;
-import com.mapbox.mapboxsdk.style.layers.Filter;
-import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.geojson.Feature;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import java.net.URL;
import java.util.ArrayList;
@@ -64,12 +64,12 @@ public class VectorSource extends Source {
* Queries the source for features.
*
* @param sourceLayerIds the source layer identifiers. At least one must be specified.
- * @param filter an optional filter statement to filter the returned Features
+ * @param filter an optional filter expression to filter the returned Features
* @return the features
*/
@NonNull
public List<Feature> querySourceFeatures(@Size(min = 1) String[] sourceLayerIds,
- @Nullable Filter.Statement filter) {
+ @Nullable Expression filter) {
Feature[] features = querySourceFeatures(
sourceLayerIds,
filter != null ? filter.toArray() : null);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java
new file mode 100644
index 0000000000..52c7014bce
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Contains the Mapbox Maps Android Text API classes.
+ */
+package com.mapbox.mapboxsdk.text;
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java
index 007880acd1..08d39d6b3b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java
@@ -1,11 +1,8 @@
package com.mapbox.mapboxsdk.utils;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.support.v4.content.ContextCompat;
-import com.mapbox.mapboxsdk.R;
import com.mapbox.mapboxsdk.constants.MapboxConstants;
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
@@ -45,25 +42,6 @@ public class MapFragmentUtils {
// load default options
options = MapboxMapOptions.createFromAttributes(context, null);
}
- options = loadDefaultMyLocationViewDrawables(context, options);
- return options;
- }
-
- private static MapboxMapOptions loadDefaultMyLocationViewDrawables(Context context, MapboxMapOptions options) {
- Drawable foregroundDrawable = options.getMyLocationForegroundDrawable();
- Drawable foregroundBearingDrawable = options.getMyLocationForegroundBearingDrawable();
- if (foregroundDrawable == null || foregroundBearingDrawable == null) {
- if (foregroundDrawable == null) {
- foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default);
- }
- if (foregroundBearingDrawable == null) {
- foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing);
- }
- options.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable);
- }
- if (options.getMyLocationBackgroundDrawable() == null) {
- options.myLocationBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape));
- }
return options;
}
}
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-public/values/public.xml b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
index 412d8c5d9b..fda37dc2df 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml
@@ -75,6 +75,7 @@
<!-- Use TextureView-->
<public name="mapbox_renderTextureMode" type="attr" />
+ <public name="mapbox_renderTextureTranslucentSurface" type="attr" />
<public name="mapbox_enableTilePrefetch" type="attr" />
<public name="mapbox_enableZMediaOverlay" type="attr" />
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
index 29ff49f47e..8dd4a858df 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml
@@ -8,12 +8,6 @@
android:background="@android:color/transparent"
android:contentDescription="@null"/>
- <com.mapbox.mapboxsdk.maps.widgets.MyLocationView
- android:id="@+id/userLocationView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:contentDescription="@string/mapbox_myLocationViewContentDescription"/>
-
<com.mapbox.mapboxsdk.maps.widgets.CompassView
android:id="@+id/compassView"
android:layout_width="wrap_content"
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-cs/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-cs/strings.xml
new file mode 100644
index 0000000000..094fb830b2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-cs/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">Kompas. Nastavit natočení mapy k severu.</string>
+ <string name="mapbox_attributionsIconContentDescription">Atributy. Zobrazit nastavení atributů.</string>
+ <string name="mapbox_myLocationViewContentDescription">Zobrazení polohy. Zobrazit umístění na mapě.</string>
+ <string name="mapbox_mapActionDescription">Zobrazení mapy vytvořené s Mapbox. Posunout tažením dvěma prsty. Změnit velikost roztažením dvou prstů.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapbox Maps SDK pro Android</string>
+ <string name="mapbox_attributionTelemetryTitle">Udělat mapy Mapbox lepšími</string>
+ <string name="mapbox_attributionTelemetryMessage">Pomáhat udělat OpenStreetMap a Mapbox mapy lepšími poskytnutím anonymních dat o využití.</string>
+ <string name="mapbox_attributionTelemetryPositive">Souhlasit</string>
+ <string name="mapbox_attributionTelemetryNegative">Odmítnout</string>
+ <string name="mapbox_attributionTelemetryNeutral">Více informací</string>
+ <string name="mapbox_attributionErrorNoBrowser">Na zařízení není nainstalován prohlížeč obsahu internetu, webovou stránku nelze zobrazit.</string>
+ <string name="mapbox_offline_error_region_definition_invalid">Pokud OfflineRegionDefinition neodpovídá hranicím: %s</string>
+ <string name="mapbox_telemetrySettings">Nastavení telemetrie</string>
+ </resources>
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-he/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml
new file mode 100644
index 0000000000..11b20f5dc5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-he/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-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-pt-rPT/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000000..02941aab5a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-pt-rPT/strings.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="mapbox_compassContentDescription">Compasso do mapa. Ativar para virar o mapa para Norte.</string>
+ <string name="mapbox_attributionsIconContentDescription">Ícone de atribuição. Ativar para mostrar a janela de atribuição.</string>
+ <string name="mapbox_myLocationViewContentDescription">Vista de localização. Isto mostra a sua localização no mapa.</string>
+ <string name="mapbox_mapActionDescription">A mostrar um Mapa criado com Mapbox. Desloque arrastanto com 2 dedos. Zoom afastando ou aproximando os 2 dedos.</string>
+ <string name="mapbox_attributionsDialogTitle">Mapas Mapbox SDK para Android</string>
+ <string name="mapbox_attributionTelemetryTitle">Torne os Mapas Mapbox Melhores</string>
+ <string name="mapbox_attributionTelemetryMessage">Está a ajudar a tornar os mapas OpenStreetMap e Mapbox melhores contribuindo com dados de utilização tornados anónimos.</string>
+ <string name="mapbox_attributionTelemetryPositive">Concordo</string>
+ <string name="mapbox_attributionTelemetryNegative">Não concordo</string>
+ <string name="mapbox_attributionTelemetryNeutral">Mais informações</string>
+ <string name="mapbox_attributionErrorNoBrowser">Não está nenhum navegador de Internet instalado no dispositivo. Não é possível abrir a página web.</string>
+ <string name="mapbox_offline_error_region_definition_invalid">O OfflineRegionDefinition não cabe nos limites do mundo: %s</string>
+ <string name="mapbox_telemetrySettings">Definições de Telemetria</string>
+ </resources>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
index 39880d56ba..a274125257 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml
@@ -1,15 +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 Android SDK</string>
+ <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 для Android</string>
<string name="mapbox_attributionTelemetryTitle">Сделать карты Mapbox лучше</string>
- <string name="mapbox_attributionTelemetryMessage">Вы помогаете сделать карты OpenStreetMap и 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_offline_error_region_definition_invalid">Запрошенный OfflineRegionDefinition не входит в допустимые границы: %s</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/main/res/values/attrs.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
index 97adce8a4e..f0b80e46e1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml
@@ -117,6 +117,7 @@
<!-- Use TextureView-->
<attr name="mapbox_renderTextureMode" format="boolean"/>
+ <attr name="mapbox_renderTextureTranslucentSurface" format="boolean"/>
<attr name="mapbox_enableTilePrefetch" format="boolean"/>
<attr name="mapbox_enableZMediaOverlay" format="boolean"/>
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
index 1c6a265587..00fc05cf6d 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml
@@ -6,4 +6,13 @@
<dimen name="mapbox_eight_dp">8dp</dimen>
<dimen name="mapbox_ninety_two_dp">92dp</dimen>
<dimen name="mapbox_my_locationview_outer_circle">18dp</dimen>
+
+ <!--Minimum scale velocity required to start animation-->
+ <dimen name="mapbox_minimum_scale_velocity">150dp</dimen>
+
+ <!--Minimum scale span delta required to execute scale gesture when rotating-->
+ <dimen name="mapbox_minimum_scale_span_when_rotating">100dp</dimen>
+
+ <!--Minimum angular velocity required to start rotation animation-->
+ <dimen name="mapbox_minimum_angular_velocity">0.025dp</dimen>
</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..6ee5c157b9 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,6 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException;
-import com.mapbox.services.android.telemetry.location.LocationEngine;
import org.junit.Before;
import org.junit.Test;
@@ -25,13 +24,11 @@ public class MapboxTest {
private Context context;
private Context appContext;
- private LocationEngine locationSource;
@Before
public void before() {
context = mock(Context.class);
appContext = mock(Context.class);
- locationSource = mock(LocationEngine.class);
when(context.getApplicationContext()).thenReturn(appContext);
}
@@ -83,7 +80,7 @@ public class MapboxTest {
}
private void injectMapboxSingleton(String accessToken) {
- Mapbox mapbox = new Mapbox(appContext, accessToken, locationSource);
+ Mapbox mapbox = new Mapbox(appContext, accessToken);
try {
Field field = Mapbox.class.getDeclaredField("INSTANCE");
field.setAccessible(true);
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..c66e4b6fda 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,103 @@ 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 zeroLongitudeSpan() {
+ latLngBounds = LatLngBounds.from(10, 10, -10, 10);
+ LatLngSpan latLngSpan = latLngBounds.getSpan();
+ assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 0),
+ 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 centerForBoundsWithSameLongitude() {
+ latLngBounds = LatLngBounds.from(10, 10, -10, 10);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(0, 10), center);
+ }
+
+ @Test
+ public void centerForBoundsWithSameLatitude() {
+ latLngBounds = LatLngBounds.from(10, 10, 10, -10);
+ LatLng center = latLngBounds.getCenter();
+ assertEquals("Center should match", new LatLng(10, 0), center);
+ }
+
+ @Test
public void center() {
LatLng center = latLngBounds.getCenter();
assertEquals("Center should match", new LatLng(1, 1), center);
@@ -118,6 +218,67 @@ public class LatLngBoundsTest {
}
@Test
+ public void includesOrderDoesNotMatter() {
+ LatLngBounds sameLongitudeFirst = new LatLngBounds.Builder()
+ .include(new LatLng(50, 10)) // southWest
+ .include(new LatLng(60, 10))
+ .include(new LatLng(60, 20)) // northEast
+ .include(new LatLng(50, 20))
+ .include(new LatLng(50, 10)) // southWest again
+ .build();
+
+ LatLngBounds sameLatitudeFirst = new LatLngBounds.Builder()
+ .include(new LatLng(50, 20))
+ .include(new LatLng(50, 10)) // southWest
+ .include(new LatLng(60, 10))
+ .include(new LatLng(60, 20)) // northEast
+ .include(new LatLng(50, 20))
+ .build();
+
+ assertEquals(sameLatitudeFirst, sameLongitudeFirst);
+ }
+
+ @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 +289,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))
@@ -204,6 +380,27 @@ public class LatLngBoundsTest {
}
@Test
+ public void unionOverDateLine() {
+ LatLngBounds latLngBounds1 = new LatLngBounds.Builder()
+ .include(new LatLng(10, 170))
+ .include(new LatLng(0, 160))
+ .build();
+
+ LatLngBounds latLngBounds2 = new LatLngBounds.Builder()
+ .include(new LatLng(0, -170))
+ .include(new LatLng(-10, -160))
+ .build();
+
+ assertEquals("outer union should match",
+ latLngBounds1.union(latLngBounds2),
+ new LatLngBounds.Builder()
+ .include(new LatLng(10, 160))
+ .include(new LatLng(-10, -160))
+ .build());
+ }
+
+
+ @Test
public void northWest() {
double minLat = 5;
double minLon = 6;
@@ -272,4 +469,100 @@ 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);
+ }
+
+ @Test
+ public void testConstructorCheckLatSouthGreaterLatNorth() {
+ exception.expect(IllegalArgumentException.class);
+ exception.expectMessage("LatSouth cannot be less than latNorth");
+ LatLngBounds.from(0, 20, 20, 0);
+ }
}
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/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java
index eeb00355bd..5de55f47c9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java
@@ -2,8 +2,13 @@ package com.mapbox.mapboxsdk.maps;
import android.graphics.PointF;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
import com.mapbox.mapboxsdk.geometry.LatLng;
+import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.mock;
@@ -13,16 +18,23 @@ import static org.mockito.Mockito.when;
public class MapTouchListenersTest {
- @Test
- public void onMapClickListenerTest() throws Exception {
- LatLng latLng = new LatLng();
- PointF pointF = new PointF();
+ private MapGestureDetector mapGestureDetector;
+ private LatLng latLng;
+ private PointF pointF;
+
+ @Before
+ public void setUp() throws Exception {
+ latLng = new LatLng();
+ pointF = new PointF();
Projection projection = mock(Projection.class);
when(projection.fromScreenLocation(pointF)).thenReturn(latLng);
- MapGestureDetector mapGestureDetector = new MapGestureDetector(null,
- null, projection, null, null, null, null);
+ mapGestureDetector = new MapGestureDetector(null,
+ null, projection, null, null, null);
+ }
+ @Test
+ public void onMapClickListenerTest() throws Exception {
MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class);
mapGestureDetector.addOnMapClickListener(listener);
mapGestureDetector.notifyOnMapClickListeners(pointF);
@@ -35,14 +47,6 @@ public class MapTouchListenersTest {
@Test
public void onMapLongClickListenerTest() throws Exception {
- LatLng latLng = new LatLng();
- PointF pointF = new PointF();
-
- Projection projection = mock(Projection.class);
- when(projection.fromScreenLocation(pointF)).thenReturn(latLng);
- MapGestureDetector mapGestureDetector = new MapGestureDetector(null,
- null, projection, null, null, null, null);
-
MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class);
mapGestureDetector.addOnMapLongClickListener(listener);
mapGestureDetector.notifyOnMapLongClickListeners(pointF);
@@ -55,14 +59,6 @@ public class MapTouchListenersTest {
@Test
public void onFlingListenerTest() throws Exception {
- LatLng latLng = new LatLng();
- PointF pointF = new PointF();
-
- Projection projection = mock(Projection.class);
- when(projection.fromScreenLocation(pointF)).thenReturn(latLng);
- MapGestureDetector mapGestureDetector = new MapGestureDetector(null,
- null, projection, null, null, null, null);
-
MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class);
mapGestureDetector.addOnFlingListener(listener);
mapGestureDetector.notifyOnFlingListeners();
@@ -75,14 +71,6 @@ public class MapTouchListenersTest {
@Test
public void onScrollListenerTest() throws Exception {
- LatLng latLng = new LatLng();
- PointF pointF = new PointF();
-
- Projection projection = mock(Projection.class);
- when(projection.fromScreenLocation(pointF)).thenReturn(latLng);
- MapGestureDetector mapGestureDetector = new MapGestureDetector(null,
- null, projection, null, null, null, null);
-
MapboxMap.OnScrollListener listener = mock(MapboxMap.OnScrollListener.class);
mapGestureDetector.addOnScrollListener(listener);
mapGestureDetector.notifyOnScrollListeners();
@@ -92,4 +80,88 @@ public class MapTouchListenersTest {
mapGestureDetector.notifyOnScrollListeners();
verify(listener, times(1)).onScroll();
}
+
+ @Test
+ public void onMoveListenerTest() throws Exception {
+ MapboxMap.OnMoveListener listener = mock(MapboxMap.OnMoveListener.class);
+ MoveGestureDetector detector = mock(MoveGestureDetector.class);
+ mapGestureDetector.addOnMoveListener(listener);
+ mapGestureDetector.notifyOnMoveBeginListeners(detector);
+ mapGestureDetector.notifyOnMoveListeners(detector);
+ mapGestureDetector.notifyOnMoveEndListeners(detector);
+ verify(listener, times(1)).onMoveBegin(detector);
+ verify(listener, times(1)).onMove(detector);
+ verify(listener, times(1)).onMoveEnd(detector);
+
+ mapGestureDetector.removeOnMoveListener(listener);
+ mapGestureDetector.notifyOnMoveBeginListeners(detector);
+ mapGestureDetector.notifyOnMoveListeners(detector);
+ mapGestureDetector.notifyOnMoveEndListeners(detector);
+ verify(listener, times(1)).onMoveBegin(detector);
+ verify(listener, times(1)).onMove(detector);
+ verify(listener, times(1)).onMoveEnd(detector);
+ }
+
+ @Test
+ public void onRotateListenerTest() throws Exception {
+ MapboxMap.OnRotateListener listener = mock(MapboxMap.OnRotateListener.class);
+ RotateGestureDetector detector = mock(RotateGestureDetector.class);
+ mapGestureDetector.addOnRotateListener(listener);
+ mapGestureDetector.notifyOnRotateBeginListeners(detector);
+ mapGestureDetector.notifyOnRotateListeners(detector);
+ mapGestureDetector.notifyOnRotateEndListeners(detector);
+ verify(listener, times(1)).onRotateBegin(detector);
+ verify(listener, times(1)).onRotate(detector);
+ verify(listener, times(1)).onRotateEnd(detector);
+
+ mapGestureDetector.removeOnRotateListener(listener);
+ mapGestureDetector.notifyOnRotateBeginListeners(detector);
+ mapGestureDetector.notifyOnRotateListeners(detector);
+ mapGestureDetector.notifyOnRotateEndListeners(detector);
+ verify(listener, times(1)).onRotateBegin(detector);
+ verify(listener, times(1)).onRotate(detector);
+ verify(listener, times(1)).onRotateEnd(detector);
+ }
+
+ @Test
+ public void onScaleListenerTest() throws Exception {
+ MapboxMap.OnScaleListener listener = mock(MapboxMap.OnScaleListener.class);
+ StandardScaleGestureDetector detector = mock(StandardScaleGestureDetector.class);
+ mapGestureDetector.addOnScaleListener(listener);
+ mapGestureDetector.notifyOnScaleBeginListeners(detector);
+ mapGestureDetector.notifyOnScaleListeners(detector);
+ mapGestureDetector.notifyOnScaleEndListeners(detector);
+ verify(listener, times(1)).onScaleBegin(detector);
+ verify(listener, times(1)).onScale(detector);
+ verify(listener, times(1)).onScaleEnd(detector);
+
+ mapGestureDetector.removeOnScaleListener(listener);
+ mapGestureDetector.notifyOnScaleBeginListeners(detector);
+ mapGestureDetector.notifyOnScaleListeners(detector);
+ mapGestureDetector.notifyOnScaleEndListeners(detector);
+ verify(listener, times(1)).onScaleBegin(detector);
+ verify(listener, times(1)).onScale(detector);
+ verify(listener, times(1)).onScaleEnd(detector);
+ }
+
+ @Test
+ public void onShoveListenerTest() throws Exception {
+ MapboxMap.OnShoveListener listener = mock(MapboxMap.OnShoveListener.class);
+ ShoveGestureDetector detector = mock(ShoveGestureDetector.class);
+ mapGestureDetector.addShoveListener(listener);
+ mapGestureDetector.notifyOnShoveBeginListeners(detector);
+ mapGestureDetector.notifyOnShoveListeners(detector);
+ mapGestureDetector.notifyOnShoveEndListeners(detector);
+ verify(listener, times(1)).onShoveBegin(detector);
+ verify(listener, times(1)).onShove(detector);
+ verify(listener, times(1)).onShoveEnd(detector);
+
+ mapGestureDetector.removeShoveListener(listener);
+ mapGestureDetector.notifyOnShoveBeginListeners(detector);
+ mapGestureDetector.notifyOnShoveListeners(detector);
+ mapGestureDetector.notifyOnShoveEndListeners(detector);
+ verify(listener, times(1)).onShoveBegin(detector);
+ verify(listener, times(1)).onShove(detector);
+ verify(listener, times(1)).onShoveEnd(detector);
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
index 65bdff41ab..9dd0ca9285 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java
@@ -119,13 +119,6 @@ public class MapboxMapOptionsTest {
}
@Test
- public void testLocationEnabled() {
- assertFalse(new MapboxMapOptions().getLocationEnabled());
- assertTrue(new MapboxMapOptions().locationEnabled(true).getLocationEnabled());
- assertFalse(new MapboxMapOptions().locationEnabled(false).getLocationEnabled());
- }
-
- @Test
public void testTiltGesturesEnabled() {
assertTrue(new MapboxMapOptions().getTiltGesturesEnabled());
assertTrue(new MapboxMapOptions().tiltGesturesEnabled(true).getTiltGesturesEnabled());
@@ -176,18 +169,6 @@ public class MapboxMapOptionsTest {
}
@Test
- public void testMyLocationForegroundTint() {
- assertEquals(Color.BLUE, new MapboxMapOptions()
- .myLocationForegroundTintColor(Color.BLUE).getMyLocationForegroundTintColor());
- }
-
- @Test
- public void testMyLocationBackgroundTint() {
- assertEquals(Color.BLUE, new MapboxMapOptions()
- .myLocationBackgroundTintColor(Color.BLUE).getMyLocationBackgroundTintColor());
- }
-
- @Test
public void testPrefetchesTiles() {
// Default value
assertTrue(new MapboxMapOptions().getPrefetchesTiles());
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
index 5e9f94db28..9a323a1d75 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java
@@ -2,7 +2,6 @@ package com.mapbox.mapboxsdk.maps;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import org.junit.After;
import org.junit.Before;
@@ -20,10 +19,8 @@ public class MapboxMapTest {
mapboxMap = new MapboxMap(mock(NativeMapView.class),
mock(Transform.class),
mock(UiSettings.class),
- mock(TrackingSettings.class),
- mock(MyLocationViewSettings.class),
mock(Projection.class),
- mock(MapboxMap.OnRegisterTouchListener.class),
+ mock(MapboxMap.OnGesturesManagerInteractionListener.class),
mock(AnnotationManager.class),
mock(CameraChangeDispatcher.class));
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
deleted file mode 100644
index de5f364a5b..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TrackingSettingsTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.graphics.PointF;
-
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class TrackingSettingsTest {
-
- @InjectMocks
- MyLocationView myLocationView = mock(MyLocationView.class);
-
- @InjectMocks
- UiSettings uiSettings = mock(UiSettings.class);
-
- @InjectMocks
- FocalPointChangeListener focalPointChangeListener = mock(FocalPointChangeListener.class);
-
- @InjectMocks
- TrackingSettings.CameraZoomInvalidator zoomInvalidator = mock(TrackingSettings.CameraZoomInvalidator.class);
-
- private TrackingSettings trackingSettings;
-
- @Before
- public void beforeTest() {
- trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointChangeListener, zoomInvalidator);
- }
-
- @Test
- public void testSanity() {
- assertNotNull("trackingsettings should not be null", trackingSettings);
- }
-
- @Test
- public void testDismissTrackingModesOnGesture() {
- trackingSettings.setDismissAllTrackingOnGesture(false);
- assertFalse("DismissTrackingOnGesture should be false", trackingSettings.isAllDismissTrackingOnGesture());
- }
-
- @Test
- public void testValidateGesturesForTrackingModes() {
- trackingSettings.setDismissAllTrackingOnGesture(false);
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- assertFalse("DismissTrackingOnGesture should be false", trackingSettings.isAllDismissTrackingOnGesture());
- }
-
- @Test
- public void testMyLocationEnabled() {
- // setup mock context to provide accepted location permission
- Context context = mock(Context.class);
- when(myLocationView.getContext()).thenReturn(context);
- when(context.checkPermission(eq(Manifest.permission.ACCESS_COARSE_LOCATION), anyInt(),
- anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
-
- assertFalse("Location should be disabled by default.", trackingSettings.isMyLocationEnabled());
- trackingSettings.setMyLocationEnabled(true);
- assertTrue("Location should be enabled", trackingSettings.isMyLocationEnabled());
- }
-
- @Test
- public void testCameraZoomTo2forTracking() {
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- verify(zoomInvalidator, atLeast(1)).zoomTo(2.0);
- }
-
- @Test
- public void testFocalPointChangeForTracking() {
- final float centerX = 32.3f;
- final float centerY = 46.3f;
- final PointF pointF = new PointF(centerX, centerY);
- when(myLocationView.getCenter()).thenReturn(pointF);
-
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- verify(focalPointChangeListener, atLeast(1)).onFocalPointChanged(pointF);
- }
-
- @Test
- public void testFocalPointChangeForNonTracking() {
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- verify(focalPointChangeListener, atLeast(1)).onFocalPointChanged(null);
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
index fbe00b4dce..cfce56e6e9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java
@@ -181,13 +181,6 @@ public class UiSettingsTest {
}
@Test
- public void testRotateGestureChange() {
- assertEquals("Default state should be true", true, uiSettings.isRotateGestureChangeAllowed());
- uiSettings.setRotateGestureChangeAllowed(false);
- assertEquals("State should have been changed", false, uiSettings.isRotateGestureChangeAllowed());
- }
-
- @Test
public void testRotateGestureChangeAllowed() {
uiSettings.setRotateGesturesEnabled(false);
assertEquals("Rotate gesture should be false", false, uiSettings.isRotateGesturesEnabled());
@@ -196,14 +189,6 @@ public class UiSettingsTest {
}
@Test
- public void testRotateGestureChangeDisallowed() {
- assertEquals("Rotate gesture should be true", true, uiSettings.isRotateGesturesEnabled());
- uiSettings.setRotateGestureChangeAllowed(false);
- uiSettings.setRotateGesturesEnabled(false);
- assertEquals("Rotate gesture change should be ignored", true, uiSettings.isRotateGesturesEnabled());
- }
-
- @Test
public void testTiltGesturesEnabled() {
uiSettings.setTiltGesturesEnabled(true);
assertEquals("Tilt gesture should be enabled", true, uiSettings.isTiltGesturesEnabled());
@@ -216,13 +201,6 @@ public class UiSettingsTest {
}
@Test
- public void testTiltGestureChange() {
- assertEquals("Default state should be true", true, uiSettings.isTiltGestureChangeAllowed());
- uiSettings.setTiltGestureChangeAllowed(false);
- assertEquals("State should have been changed", false, uiSettings.isTiltGestureChangeAllowed());
- }
-
- @Test
public void testTiltGestureChangeAllowed() {
uiSettings.setTiltGesturesEnabled(false);
assertEquals("Tilt gesture should be false", false, uiSettings.isTiltGesturesEnabled());
@@ -231,14 +209,6 @@ public class UiSettingsTest {
}
@Test
- public void testTiltGestureChangeDisallowed() {
- assertEquals("Tilt gesture should be true", true, uiSettings.isTiltGesturesEnabled());
- uiSettings.setTiltGestureChangeAllowed(false);
- uiSettings.setTiltGesturesEnabled(false);
- assertEquals("Tilt gesture change should be ignored", true, uiSettings.isTiltGesturesEnabled());
- }
-
- @Test
public void testZoomGesturesEnabled() {
uiSettings.setZoomGesturesEnabled(true);
assertEquals("Zoom gesture should be enabled", true, uiSettings.isZoomGesturesEnabled());
@@ -251,13 +221,6 @@ public class UiSettingsTest {
}
@Test
- public void testZoomGestureChange() {
- assertEquals("Default state should be true", true, uiSettings.isZoomGestureChangeAllowed());
- uiSettings.setZoomGestureChangeAllowed(false);
- assertEquals("State should have been changed", false, uiSettings.isZoomGestureChangeAllowed());
- }
-
- @Test
public void testZoomGestureChangeAllowed() {
uiSettings.setZoomGesturesEnabled(false);
assertEquals("Zoom gesture should be false", false, uiSettings.isZoomGesturesEnabled());
@@ -266,14 +229,6 @@ public class UiSettingsTest {
}
@Test
- public void testZoomGestureChangeDisallowed() {
- assertEquals("Zoom gesture should be true", true, uiSettings.isZoomGesturesEnabled());
- uiSettings.setZoomGestureChangeAllowed(false);
- uiSettings.setZoomGesturesEnabled(false);
- assertEquals("Zooom gesture change should be ignored", true, uiSettings.isZoomGesturesEnabled());
- }
-
- @Test
public void testZoomControlsEnabled() {
uiSettings.setZoomControlsEnabled(true);
assertEquals("Zoom controls should be enabled", true, uiSettings.isZoomControlsEnabled());
@@ -298,13 +253,6 @@ public class UiSettingsTest {
}
@Test
- public void testDoubleTapGestureChange() {
- assertEquals("Default state should be true", true, uiSettings.isDoubleTapGestureChangeAllowed());
- uiSettings.setDoubleTapGestureChangeAllowed(false);
- assertEquals("State should have been changed", false, uiSettings.isDoubleTapGestureChangeAllowed());
- }
-
- @Test
public void testDoubleTapGestureChangeAllowed() {
uiSettings.setDoubleTapGesturesEnabled(false);
assertEquals("DoubleTap gesture should be false", false, uiSettings.isDoubleTapGesturesEnabled());
@@ -313,14 +261,6 @@ public class UiSettingsTest {
}
@Test
- public void testDoubleTapGestureChangeDisallowed() {
- assertEquals("DoubleTap gesture should be true", true, uiSettings.isDoubleTapGesturesEnabled());
- uiSettings.setDoubleTapGestureChangeAllowed(false);
- uiSettings.setDoubleTapGesturesEnabled(false);
- assertEquals("DoubleTap gesture change should be ignored", true, uiSettings.isDoubleTapGesturesEnabled());
- }
-
- @Test
public void testScrollGesturesEnabled() {
uiSettings.setScrollGesturesEnabled(true);
assertEquals("Scroll gesture should be enabled", true, uiSettings.isScrollGesturesEnabled());
@@ -333,13 +273,6 @@ public class UiSettingsTest {
}
@Test
- public void testScrollGestureChange() {
- assertEquals("Default state should be true", true, uiSettings.isScrollGestureChangeAllowed());
- uiSettings.setScrollGestureChangeAllowed(false);
- assertEquals("State should have been changed", false, uiSettings.isScrollGestureChangeAllowed());
- }
-
- @Test
public void testScrollGestureChangeAllowed() {
uiSettings.setScrollGesturesEnabled(false);
assertEquals("Scroll gesture should be false", false, uiSettings.isScrollGesturesEnabled());
@@ -348,11 +281,82 @@ public class UiSettingsTest {
}
@Test
- public void testScrollGestureChangeDisallowed() {
- assertEquals("Scroll gesture should be true", true, uiSettings.isScrollGesturesEnabled());
- uiSettings.setScrollGestureChangeAllowed(false);
- uiSettings.setScrollGesturesEnabled(false);
- assertEquals("Scroll gesture change should be ignored", true, uiSettings.isScrollGesturesEnabled());
+ public void testScaleVelocityAnimationEnabled() {
+ uiSettings.setScaleVelocityAnimationEnabled(true);
+ assertEquals("Scale velocity animation should be enabled", true, uiSettings.isScaleVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testScaleVelocityAnimationDisabled() {
+ uiSettings.setScaleVelocityAnimationEnabled(false);
+ assertEquals("Scale velocity animation should be disabled", false, uiSettings.isScaleVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testRotateVelocityAnimationEnabled() {
+ uiSettings.setRotateVelocityAnimationEnabled(true);
+ assertEquals("Rotate velocity animation should be enabled", true, uiSettings.isRotateVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testRotateVelocityAnimationDisabled() {
+ uiSettings.setRotateVelocityAnimationEnabled(false);
+ assertEquals("Rotate velocity animation should be disabled", false, uiSettings.isRotateVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testFlingVelocityAnimationEnabled() {
+ uiSettings.setFlingVelocityAnimationEnabled(true);
+ assertEquals("Fling velocity animation should be enabled", true, uiSettings.isFlingVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testFlingVelocityAnimationDisabled() {
+ uiSettings.setFlingVelocityAnimationEnabled(false);
+ assertEquals("Fling velocity animation should be disabled", false, uiSettings.isFlingVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testAllVelocityAnimationsEnabled() {
+ uiSettings.setAllVelocityAnimationsEnabled(true);
+ assertEquals("Scale velocity animation should be enabled", true, uiSettings.isScaleVelocityAnimationEnabled());
+ assertEquals("Rotate velocity animation should be enabled", true, uiSettings.isRotateVelocityAnimationEnabled());
+ assertEquals("Fling velocity animation should be enabled", true, uiSettings.isFlingVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testAllVelocityAnimationsDisabled() {
+ uiSettings.setAllVelocityAnimationsEnabled(false);
+ assertEquals("Scale velocity animation should be disabled", false, uiSettings.isScaleVelocityAnimationEnabled());
+ assertEquals("Rotate velocity animation should be disabled", false, uiSettings.isRotateVelocityAnimationEnabled());
+ assertEquals("Fling velocity animation should be disabled", false, uiSettings.isFlingVelocityAnimationEnabled());
+ }
+
+ @Test
+ public void testIncreaseRotateThresholdWhenScalingEnabled() {
+ uiSettings.setIncreaseRotateThresholdWhenScaling(true);
+ assertEquals("Rotate threshold increase should be enabled", true,
+ uiSettings.isIncreaseRotateThresholdWhenScaling());
+ }
+
+ @Test
+ public void testIncreaseRotateThresholdWhenScalingDisabled() {
+ uiSettings.setIncreaseRotateThresholdWhenScaling(false);
+ assertEquals("Rotate threshold increase should be disabled", false,
+ uiSettings.isIncreaseRotateThresholdWhenScaling());
+ }
+
+ @Test
+ public void testIncreaseScaleThresholdWhenRotatingEnabled() {
+ uiSettings.setIncreaseScaleThresholdWhenRotating(true);
+ assertEquals("Scale threshold increase should be enabled", true, uiSettings.isIncreaseScaleThresholdWhenRotating());
+ }
+
+ @Test
+ public void testIncreaseScaleThresholdWhenRotatingDisabled() {
+ uiSettings.setIncreaseScaleThresholdWhenRotating(false);
+ assertEquals("Scale threshold increase should be disabled", false,
+ uiSettings.isIncreaseScaleThresholdWhenRotating());
}
@Test
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
deleted file mode 100644
index c9ce19dc85..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettingsTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.mapbox.mapboxsdk.maps.widgets;
-
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-
-import com.mapbox.mapboxsdk.maps.FocalPointChangeListener;
-import com.mapbox.mapboxsdk.maps.Projection;
-import com.mapbox.mapboxsdk.maps.TrackingSettings;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.InjectMocks;
-
-import java.util.Arrays;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class MyLocationViewSettingsTest {
-
- @InjectMocks
- Projection projection = mock(Projection.class);
-
- @InjectMocks
- MyLocationView myLocationView = mock(MyLocationView.class);
-
- @InjectMocks
- TrackingSettings trackingSettings = mock(TrackingSettings.class);
-
- @InjectMocks
- FocalPointChangeListener focalPointChangeListener = mock(FocalPointChangeListener.class);
-
- private MyLocationViewSettings locationViewSettings;
-
- @Before
- public void beforeTest() {
- locationViewSettings = new MyLocationViewSettings(myLocationView, projection, focalPointChangeListener);
- }
-
- @Test
- public void testSanity() {
- assertNotNull("should not be null", locationViewSettings);
- }
-
- @Test
- public void testForegroundDrawables() {
- Drawable foregroundDrawable = mock(Drawable.class);
- Drawable foregroundBearingDrawable = mock(Drawable.class);
- Drawable.ConstantState constantState = mock(Drawable.ConstantState.class);
- when(foregroundDrawable.getConstantState()).thenReturn(constantState);
- when(constantState.newDrawable()).thenReturn(foregroundDrawable);
- locationViewSettings.setForegroundDrawable(foregroundDrawable, foregroundBearingDrawable);
- assertEquals("foreground should match", foregroundDrawable, locationViewSettings.getForegroundDrawable());
- assertEquals("foreground bearing should match", foregroundBearingDrawable,
- locationViewSettings.getForegroundBearingDrawable());
- }
-
- @Test
- public void testBackgroundDrawable() {
- Drawable backgroundDrawable = mock(Drawable.class);
- int[] offset = new int[] {1, 2, 3, 4};
- locationViewSettings.setBackgroundDrawable(backgroundDrawable, offset);
- assertEquals("foreground should match", backgroundDrawable, locationViewSettings.getBackgroundDrawable());
- assertTrue("offsets should match", Arrays.equals(offset, locationViewSettings.getBackgroundOffset()));
- }
-
- @Test
- public void testForegroundTint() {
- int color = Color.RED;
- locationViewSettings.setForegroundTintColor(Color.RED);
- assertEquals("color should match", color, locationViewSettings.getForegroundTintColor());
- }
-
- @Test
- public void testForegroundTransparentTint() {
- int color = Color.TRANSPARENT;
- locationViewSettings.setForegroundTintColor(Color.TRANSPARENT);
- assertEquals("color should match", color, locationViewSettings.getForegroundTintColor());
- }
-
- @Test
- public void testBackgroundTint() {
- int color = Color.RED;
- locationViewSettings.setBackgroundTintColor(Color.RED);
- assertEquals("color should match", color, locationViewSettings.getBackgroundTintColor());
- }
-
- @Test
- public void testBackgroundTransparentTint() {
- int color = Color.TRANSPARENT;
- locationViewSettings.setBackgroundTintColor(Color.TRANSPARENT);
- assertEquals("color should match", color, locationViewSettings.getBackgroundTintColor());
- }
-
- @Test
- public void testEnabled() {
- assertFalse("initial state should be false", locationViewSettings.isEnabled());
- locationViewSettings.setEnabled(true);
- assertTrue("state should be true", locationViewSettings.isEnabled());
- }
-}
-
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..45833e8556
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java
@@ -0,0 +1,1168 @@
+package com.mapbox.mapboxsdk.style.expressions;
+
+import android.graphics.Color;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.abs;
+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.ceil;
+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.floor;
+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.round;
+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 com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity;
+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", new Object[] {"to-color", "rgba(255, 0, 0, 255)"}};
+ 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(literal(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(literal(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(literal(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(literal(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(literal(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(literal(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 {
+ String input = "input";
+ String[] labels = new String[] {"a", "b", "c"};
+ String[] outputs = new String[] {"1", "2", "3"};
+ String defaultOutput = "0";
+
+ Object[] expected = new Object[] {"match", input,
+ labels[0], outputs[0],
+ labels[1], outputs[1],
+ labels[2], outputs[2],
+ defaultOutput};
+
+ Object[] actual = match(literal(input),
+ literal(labels[0]), literal(outputs[0]),
+ literal(labels[1]), literal(outputs[1]),
+ literal(labels[2]), literal(outputs[2]),
+ literal(defaultOutput)
+ ).toArray();
+
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testMatchWithStops() throws Exception {
+ String input = "input";
+ String[] labels = new String[] {"a", "b", "c"};
+ String[] outputs = new String[] {"1", "2", "3"};
+ String defaultOutput = "0";
+
+ Object[] expected = new Object[] {"match", input,
+ labels[0], outputs[0],
+ labels[1], outputs[1],
+ labels[2], outputs[2],
+ defaultOutput};
+
+ Object[] actual = match(literal(input), literal(defaultOutput),
+ stop(labels[0], outputs[0]),
+ stop(labels[1], outputs[1]),
+ stop(labels[2], outputs[2]))
+ .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[] {"literal", 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[] {"literal", 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[] expected = new Object[] {"interpolate", new Object[] {"linear"}, 12, 0, 1, 1, 2, 2, 3};
+ Object[] actual = interpolate(
+ linear(), literal(12),
+ literal(0), literal(1),
+ literal(1), literal(2),
+ literal(2), literal(3))
+ .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);
+ }
+
+ @Test
+ public void testLiteralArray() throws Exception {
+ Object[] array = new Object[] {1, "text"};
+ Object[] expected = new Object[] {"literal", array};
+ Object[] actual = literal(array).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testLiteralArrayString() throws Exception {
+ Object[] array = new Object[] {1, "text"};
+ String expected = "[\"literal\"], [1, \"text\"]]";
+ String actual = literal(array).toString();
+ assertEquals("literal array should match", expected, actual);
+ }
+
+ @Test
+ public void testLiteralPrimitiveArrayConversion() throws Exception {
+ float[] array = new float[] {0.2f, 0.5f};
+ Object[] expected = new Object[] {"literal", new Object[] {0.2f, 0.5f}};
+ Object[] actual = literal(array).toArray();
+ assertEquals("primitive array should be converted", expected, actual);
+ }
+
+ @Test
+ public void testColorConversion() {
+ Expression greenColor = color(0xFF00FF00);
+ Object[] expected = new Object[] {"to-color", "rgba(0, 255, 0, 255)"};
+ assertTrue("expression should match", Arrays.deepEquals(expected, greenColor.toArray()));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testThrowIllegalArgumentExceptionForPropertyValueLiteral() {
+ Expression expression = interpolate(exponential(1f), zoom(),
+ stop(17f, lineOpacity(1f)),
+ stop(16.5f, lineOpacity(0.5f)),
+ stop(16f, lineOpacity(0f))
+ );
+ expression.toArray();
+ }
+
+ @Test
+ public void testRound() {
+ Object[] expected = new Object[] {"round", 2.2f};
+ Object[] actual = round(2.2f).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testRoundLiteral() {
+ Object[] expected = new Object[] {"round", 2.2f};
+ Object[] actual = round(literal(2.2f)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAbs() {
+ Object[] expected = new Object[] {"abs", -2.2f};
+ Object[] actual = abs(-2.2f).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testAbsLiteral() {
+ Object[] expected = new Object[] {"abs", -2.2f};
+ Object[] actual = abs(literal(-2.2f)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCeil() {
+ Object[] expected = new Object[] {"ceil", 2.2f};
+ Object[] actual = ceil(2.2f).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testCeilLiteral() {
+ Object[] expected = new Object[] {"ceil", 2.2f};
+ Object[] actual = ceil(literal(2.2f)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testFloor() {
+ Object[] expected = new Object[] {"floor", 2.2f};
+ Object[] actual = floor(2.2f).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+
+ @Test
+ public void testFloorLiteral() {
+ Object[] expected = new Object[] {"floor", 2.2f};
+ Object[] actual = floor(literal(2.2f)).toArray();
+ assertTrue("expression should match", Arrays.deepEquals(expected, actual));
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
deleted file mode 100644
index 933bf05b39..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package com.mapbox.mapboxsdk.style.layers;
-
-import org.junit.Test;
-
-import static com.mapbox.mapboxsdk.style.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.any;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gt;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.has;
-import static com.mapbox.mapboxsdk.style.layers.Filter.in;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.neq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.none;
-import static com.mapbox.mapboxsdk.style.layers.Filter.notHas;
-import static com.mapbox.mapboxsdk.style.layers.Filter.notIn;
-import static org.junit.Assert.assertArrayEquals;
-
-/**
- * Tests for Filter
- */
-public class FilterTest {
-
- @Test
- public void testAll() {
- assertArrayEquals(all().toArray(), new Object[] {"all"});
- assertArrayEquals(
- all(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"all", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testAny() {
- assertArrayEquals(any().toArray(), new Object[] {"any"});
- assertArrayEquals(
- any(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"any", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testNone() {
- assertArrayEquals(none().toArray(), new Object[] {"none"});
- assertArrayEquals(
- none(eq("key", 2), neq("key", 3)).toArray(),
- new Object[] {"none", new Object[] {"==", "key", 2}, new Object[] {"!=", "key", 3}}
- );
- }
-
- @Test
- public void testHas() {
- assertArrayEquals(has("key").toArray(), new Object[] {"has", "key"});
- }
-
- @Test
- public void testHasNot() {
- assertArrayEquals(notHas("key").toArray(), new Object[] {"!has", "key"});
- }
-
- @Test
- public void testEq() {
- assertArrayEquals(eq("key", 1).toArray(), new Object[] {"==", "key", 1});
-
- }
-
- @Test
- public void testNeq() {
- assertArrayEquals(neq("key", 1).toArray(), new Object[] {"!=", "key", 1});
- }
-
- @Test
- public void testGt() {
- assertArrayEquals(gt("key", 1).toArray(), new Object[] {">", "key", 1});
- }
-
- @Test
- public void testGte() {
- assertArrayEquals(gte("key", 1).toArray(), new Object[] {">=", "key", 1});
- }
-
- @Test
- public void testLt() {
- assertArrayEquals(lt("key", 1).toArray(), new Object[] {"<", "key", 1});
- }
-
- @Test
- public void testLte() {
- assertArrayEquals(lte("key", 1).toArray(), new Object[] {"<=", "key", 1});
- }
-
- @Test
- public void testIn() {
- assertArrayEquals(in("key", 1, 2, "Aap").toArray(), new Object[] {"in", "key", 1, 2, "Aap"});
- }
-
- @Test
- public void testNotIn() {
- assertArrayEquals(notIn("key", 1, 2, "Noot").toArray(), new Object[] {"!in", "key", 1, 2, "Noot"});
- }
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
deleted file mode 100644
index bac1154d62..0000000000
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/layers/FunctionTest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.mapbox.mapboxsdk.style.layers;
-
-import com.mapbox.mapboxsdk.style.functions.Function;
-
-import org.junit.Test;
-
-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.interval;
-import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineBlur;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * Tests Function
- */
-public class FunctionTest {
-
- @Test
- public void testZoomFunction() {
- Function<Float, Float> zoomF = zoom(interval(
- stop(1f, lineBlur(1f)),
- stop(10f, lineBlur(20f))
- )
- );
-
- assertNotNull(zoomF.toValueObject());
- assertArrayEquals(
- new Object[] {new Object[] {1f, 1f}, new Object[] {10f, 20f}},
- (Object[]) zoomF.toValueObject().get("stops")
- );
- }
-
-}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
index 5b54496329..519124e1eb 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java
@@ -1,8 +1,9 @@
package com.mapbox.mapboxsdk.telemetry;
+import com.mapbox.android.telemetry.TelemetryUtils;
+
import org.junit.Test;
-import static com.mapbox.services.android.telemetry.utils.TelemetryUtils.toHumanReadableAscii;
import static junit.framework.Assert.assertEquals;
public class HttpTransportTest {
@@ -14,6 +15,6 @@ public class HttpTransportTest {
final String asciiVersion = "Sveriges Fj?ll/1.0/1 MapboxEventsAndroid/4.0.0-SNAPSHOT";
assertEquals("asciiVersion and swedishUserAgent should match", asciiVersion,
- toHumanReadableAscii(swedishUserAgent));
+ TelemetryUtils.toHumanReadableAscii(swedishUserAgent));
}
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index f45ad3dc3a..80be26d3ae 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 13
+ 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
}
@@ -39,8 +39,9 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
- minifyEnabled false
+ minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ signingConfig signingConfigs.debug
}
}
@@ -52,36 +53,32 @@ 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
+ androidTestImplementation dependenciesList.testEspressoContrib
}
-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/MapViewUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java
deleted file mode 100644
index 38d5297291..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapViewUtils.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package com.mapbox.mapboxsdk.maps;
-
-import com.mapbox.mapboxsdk.geometry.LatLng;
-
-/**
- * Utility class to bypass package visibility
- */
-public class MapViewUtils {
-
- public static void setDirection(MapboxMap mapboxMap, float direction) {
- mapboxMap.getTransform().setBearing(direction);
- }
-
- public static float getDirection(MapboxMap mapboxMap) {
- return (float) mapboxMap.getTransform().getBearing();
- }
-
- public static void setTilt(MapboxMap mapboxMap, float tilt) {
- mapboxMap.getTransform().setTilt((double) tilt);
- }
-
- public static float getTilt(MapboxMap mapboxMap) {
- return (float) mapboxMap.getTransform().getTilt();
- }
-
- public static void setLatLng(MapboxMap mapboxMap, LatLng latLng) {
- mapboxMap.getTransform().setCenterCoordinate(latLng);
- }
-
- public static LatLng getLatLng(MapboxMap mapboxMap) {
- return mapboxMap.getTransform().getCenterCoordinate();
- }
-}
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..38fd8491a8 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,101 +153,43 @@ 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);
}));
}
//
- // TrackingSettings
- //
-
- @Test
- public void testTrackingSettings() {
- validateTestSetup();
- assertNotNull("TrackingSettings should not be null", mapboxMap.getTrackingSettings());
- }
-
- //
// InfoWindow
//
@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());
- }
- }));
- }
-
- //
- // Location
- //
-
- @Test
- @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());
- }
- }));
- }
-
- @Test
- @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.InfoWindowAdapter infoWindowAdapter = marker -> null;
+ mapboxMap.setInfoWindowAdapter(infoWindowAdapter);
+ assertEquals("InfoWindowAdpter should be the same", infoWindowAdapter, mapboxMap.getInfoWindowAdapter());
}));
}
@@ -274,73 +200,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 +253,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 +268,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 +626,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..89397c30eb
--- /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());
+ waitAction(2200);
+ onView(isRoot()).perform(orientationPortrait());
+ waitAction(2500);
+ onView(isRoot()).perform(orientationLandscapeReverse());
+ waitAction(500);
+ onView(isRoot()).perform(orientationPortraitReverse());
+ waitAction(1250);
+ onView(isRoot()).perform(orientationLandscape());
+ waitAction(750);
+ onView(isRoot()).perform(orientationPortrait());
+ waitAction(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/action/WaitAction.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java
new file mode 100644
index 0000000000..26a3a2e4ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java
@@ -0,0 +1,39 @@
+package com.mapbox.mapboxsdk.testapp.action;
+
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.view.View;
+
+import org.hamcrest.Matcher;
+
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+
+public final class WaitAction implements ViewAction {
+
+ private static final long DEFAULT_LOOP_TIME = 375;
+ private final long loopTime;
+
+ public WaitAction() {
+ this(DEFAULT_LOOP_TIME);
+ }
+
+ public WaitAction(long loopTime) {
+ this.loopTime = loopTime;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ uiController.loopMainThreadForAtLeast(loopTime);
+ }
+}
+
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..6d90c20a46 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
@@ -6,18 +6,15 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.IdlingResourceTimeoutException;
-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.action.WaitAction;
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;
@@ -71,8 +68,12 @@ public abstract class BaseActivityTest {
onView(withId(id)).check(matches(isDisplayed()));
}
- protected void waitLoop() {
- onView(withId(R.id.mapView)).perform(new LoopAction(500));
+ protected void waitAction() {
+ waitAction(500);
+ }
+
+ protected void waitAction(long waitTime) {
+ onView(withId(R.id.mapView)).perform(new WaitAction(waitTime));
}
static boolean isConnected(Context context) {
@@ -87,29 +88,5 @@ public abstract class BaseActivityTest {
Timber.e("@After test: unregister idle resource");
Espresso.unregisterIdlingResources(idlingResource);
}
-
- private class LoopAction implements ViewAction {
-
- private long loopTime;
-
- public LoopAction(long loopTime) {
- this.loopTime = loopTime;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- uiController.loopMainThreadForAtLeast(loopTime);
- }
- }
}
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/CameraInternalApiTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java
deleted file mode 100644
index 883e76653d..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraInternalApiTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.camera;
-
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapViewUtils;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-import com.mapbox.mapboxsdk.testapp.utils.TestConstants;
-
-import org.hamcrest.Matcher;
-import org.junit.After;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.junit.Assert.assertEquals;
-
-/**
- * Tests camera transformations that aren't part of our public API
- */
-public class CameraInternalApiTest extends BaseActivityTest {
-
- @Override
- protected Class getActivityClass() {
- return EspressoTestActivity.class;
- }
-
- @Test
- @Ignore
- public void testBearing() {
- validateTestSetup();
-
- CameraPosition initialPosition = new
- CameraPosition.Builder().target(new LatLng()).zoom(1).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- onView(withId(R.id.mapView)).perform(new BearingAction(mapboxMap));
- assertEquals("Bearing should match", 45.1f, MapViewUtils.getDirection(mapboxMap), TestConstants.BEARING_DELTA);
- }
-
- @Test
- @Ignore
- public void testTilt() {
- validateTestSetup();
-
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(1).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- onView(withId(R.id.mapView)).perform(new TiltAction(mapboxMap));
- assertEquals("Tilt should match", 40.0f, MapViewUtils.getTilt(mapboxMap), TestConstants.TILT_DELTA);
- }
-
- @Test
- @Ignore
- public void testLatLng() {
- validateTestSetup();
-
- CameraPosition initialPosition = new CameraPosition.Builder().target(
- new LatLng()).zoom(1).bearing(0).tilt(0).build();
- CameraPosition cameraPosition = mapboxMap.getCameraPosition();
- assertEquals("Default camera position should match default", cameraPosition, initialPosition);
-
- onView(withId(R.id.mapView)).perform(new LatLngAction(mapboxMap));
- LatLng centerCoordinate = MapViewUtils.getLatLng(mapboxMap);
- assertEquals("Latitude should match", 1.1f, centerCoordinate.getLatitude(), TestConstants.LAT_LNG_DELTA);
- assertEquals("Longitude should match", 2.2f, centerCoordinate.getLongitude(), TestConstants.LAT_LNG_DELTA);
- }
-
- @After
- public void unregisterIdlingResource() {
- Espresso.unregisterIdlingResources(idlingResource);
- }
-
- private class BearingAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- BearingAction(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- MapViewUtils.setDirection(mapboxMap, -45.1f);
- }
- }
-
- private class TiltAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- TiltAction(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- MapViewUtils.setTilt(mapboxMap, 40.0f);
- }
- }
-
- private class LatLngAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- LatLngAction(MapboxMap mapboxMap) {
- this.mapboxMap = mapboxMap;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- MapViewUtils.setLatLng(mapboxMap, new LatLng(1.1, 2.2));
- }
- }
-}
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..26aee2de98 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();
+ waitAction();
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/maps/widgets/MyLocationViewTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
deleted file mode 100644
index cf58ba50a6..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/MyLocationViewTest.java
+++ /dev/null
@@ -1,229 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.maps.widgets;
-
-import android.annotation.SuppressLint;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-import com.mapbox.mapboxsdk.constants.MyLocationTracking;
-import com.mapbox.mapboxsdk.geometry.LatLng;
-import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.widgets.MyLocationView;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
-import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
-
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static org.hamcrest.Matchers.not;
-
-/**
- * Experimental MyLocationView tests,
- * requires application to be granted with runtime location permissions.
- * <p>
- * Tests for enabling and disabling the {@link MyLocationView}.
- * Tests for enabling tracking modes and if the correct default images are shown when toggling
- * {@link com.mapbox.mapboxsdk.maps.TrackingSettings#setMyLocationTrackingMode(int)} &
- * {@link com.mapbox.mapboxsdk.maps.TrackingSettings#setMyBearingTrackingMode(int)}.
- * </p>
- */
-public class MyLocationViewTest extends BaseActivityTest {
-
- @Override
- protected Class getActivityClass() {
- return EspressoTestActivity.class;
- }
-
- @Test
- @Ignore // requires runtime permissions, disabled for CI
- public void testEnabled() {
- validateTestSetup();
- onView(withId(R.id.userLocationView)).check(matches(not(isDisplayed())));
- onView(withId(R.id.mapView)).perform(new ToggleLocationAction(mapboxMap, true));
- onView(withId(R.id.userLocationView)).check(matches(isDisplayed()));
- onView(withId(R.id.mapView)).perform(new ToggleLocationAction(mapboxMap, false));
- onView(withId(R.id.userLocationView)).check(matches(not(isDisplayed())));
- }
-
- @Test
- @Ignore
- // requires runtime permissions, disabled for CI + issue with android.support.test.espresso.AppNotIdleException:
- // Looped for 5049 iterations over 60 SECONDS.
- public void testTracking() {
- validateTestSetup();
- onView(withId(R.id.userLocationView)).check(matches(not(isDisplayed())));
- onView(withId(R.id.mapView)).perform(new EnableLocationTrackingAction(mapboxMap));
- onView(withId(R.id.userLocationView)).check(matches(isDisplayed()));
- onView(withId(R.id.userLocationView)).check(matches(new DrawableMatcher(mapboxMap,
- R.drawable.mapbox_mylocation_icon_default, false)));
- onView(withId(R.id.mapView)).perform(new EnableCompassBearingTrackingAction(mapboxMap));
- onView(withId(R.id.userLocationView)).check(matches(new DrawableMatcher(mapboxMap,
- R.drawable.mapbox_mylocation_icon_bearing, true)));
- }
-
- private class ToggleLocationAction implements ViewAction {
-
- private MapboxMap mapboxMap;
- private boolean isEnabled;
-
- ToggleLocationAction(MapboxMap map, boolean enable) {
- mapboxMap = map;
- isEnabled = enable;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @SuppressLint("MissingPermission")
- @Override
- public void perform(UiController uiController, View view) {
- if (isEnabled) {
- // move camera above user location
- mapboxMap.moveCamera(
- CameraUpdateFactory.newCameraPosition(
- new CameraPosition.Builder()
- .target(new LatLng(Mapbox.getLocationEngine().getLastLocation()))
- .build()
- )
- );
- }
-
- // show loction on screen
- mapboxMap.setMyLocationEnabled(isEnabled);
- }
- }
-
- private class EnableLocationTrackingAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- EnableLocationTrackingAction(MapboxMap map) {
- mapboxMap = map;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.getTrackingSettings().setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- }
- }
-
- private class EnableCompassBearingTrackingAction implements ViewAction {
-
- private MapboxMap mapboxMap;
-
- EnableCompassBearingTrackingAction(MapboxMap map) {
- mapboxMap = map;
- }
-
- @Override
- public Matcher<View> getConstraints() {
- return isDisplayed();
- }
-
- @Override
- public String getDescription() {
- return getClass().getSimpleName();
- }
-
- @Override
- public void perform(UiController uiController, View view) {
- mapboxMap.getTrackingSettings().setMyBearingTrackingMode(MyBearingTracking.COMPASS);
- // wait for next compass update cycle
- uiController.loopMainThreadForAtLeast(500);
- }
- }
-
- private class DrawableMatcher extends TypeSafeMatcher<View> {
-
- private MapboxMap mapboxMap;
- private boolean isBearingDrawable;
- private final int expectedId;
-
- DrawableMatcher(MapboxMap mapboxMap, int expectedId, boolean isBearingDrawable) {
- super(MyLocationView.class);
- this.mapboxMap = mapboxMap;
- this.expectedId = expectedId;
- this.isBearingDrawable = isBearingDrawable;
- }
-
- @Override
- protected boolean matchesSafely(View target) {
- Drawable currentDrawable = isBearingDrawable
- ? mapboxMap.getMyLocationViewSettings().getForegroundBearingDrawable() :
- mapboxMap.getMyLocationViewSettings().getForegroundDrawable();
-
- Resources resources = target.getContext().getResources();
- Drawable expectedDrawable = resources.getDrawable(expectedId);
- return areDrawablesIdentical(currentDrawable, expectedDrawable);
- }
-
- @Override
- public void describeTo(Description description) {
- description.appendText("trying to match MyLocationView drawable to " + expectedId);
- }
-
- boolean areDrawablesIdentical(Drawable drawableA, Drawable drawableB) {
- Drawable.ConstantState stateA = drawableA.getConstantState();
- Drawable.ConstantState stateB = drawableB.getConstantState();
- return (stateA != null && stateB != null && stateA.equals(stateB))
- || getBitmap(drawableA).sameAs(getBitmap(drawableB));
- }
-
- Bitmap getBitmap(Drawable drawable) {
- Bitmap result;
- if (drawable instanceof BitmapDrawable) {
- result = ((BitmapDrawable) drawable).getBitmap();
- } else {
- int width = drawable.getIntrinsicWidth();
- int height = drawable.getIntrinsicHeight();
- // Some drawables have no intrinsic width - e.g. solid colours.
- if (width <= 0) {
- width = 1;
- }
- if (height <= 0) {
- height = 1;
- }
-
- result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- }
- return result;
- }
- }
-}
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/storage/FileSourceTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java
new file mode 100644
index 0000000000..554bc988a6
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTest.java
@@ -0,0 +1,129 @@
+package com.mapbox.mapboxsdk.testapp.storage;
+
+import android.os.Looper;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
+import com.mapbox.mapboxsdk.storage.FileSource;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.action.WaitAction;
+import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity;
+
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.mapbox.mapboxsdk.testapp.action.OrientationChangeAction.orientationLandscape;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class FileSourceTest {
+
+ @Rule
+ public ActivityTestRule<FeatureOverviewActivity> rule = new ActivityTestRule<>(FeatureOverviewActivity.class);
+
+ private FileSource fileSource;
+
+ @Before
+ public void setUp() throws Exception {
+ onView(withId(R.id.recyclerView)).perform(new FileSourceCreator());
+ }
+
+ @Test
+ public void testDefault() throws Exception {
+ assertFalse("FileSource should not be active", fileSource.isActivated());
+ }
+
+ @Test
+ public void testActivateDeactivate() throws Exception {
+ assertFalse("1) FileSource should not be active", fileSource.isActivated());
+ onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(true));
+ assertTrue("2) FileSource should be active", fileSource.isActivated());
+ onView(withId(R.id.recyclerView)).perform(new FileSourceActivator(false));
+ assertFalse("3) FileSource should not be active", fileSource.isActivated());
+ }
+
+ @Test
+ public void testOpenCloseMapView() throws Exception {
+ assertFalse("1) FileSource should not be active", fileSource.isActivated());
+ onView(withText("Simple Map")).perform(click());
+ onView(withId(R.id.mapView)).perform(new WaitAction());
+ assertTrue("2) FileSource should be active", fileSource.isActivated());
+ onView(withId(R.id.mapView)).perform(new WaitAction());
+ pressBack();
+ assertFalse("3) FileSource should not be active", fileSource.isActivated());
+ }
+
+ @Test
+ public void testRotateMapView() throws Exception {
+ assertFalse("1) FileSource should not be active", fileSource.isActivated());
+ onView(withText("Simple Map")).perform(click());
+ onView(withId(R.id.mapView)).perform(new WaitAction());
+ onView(isRoot()).perform(orientationLandscape());
+ onView(withId(R.id.mapView)).perform(new WaitAction());
+ assertTrue("2) FileSource should be active", fileSource.isActivated());
+ onView(withId(R.id.mapView)).perform(new WaitAction());
+ pressBack();
+ assertFalse("3) FileSource should not be active", fileSource.isActivated());
+ }
+
+ private class FileSourceCreator implements ViewAction {
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Creates the filesource instance on the UI thread";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ assertTrue(Looper.myLooper() == Looper.getMainLooper());
+ fileSource = FileSource.getInstance(rule.getActivity());
+ }
+ }
+
+ private class FileSourceActivator implements ViewAction {
+
+ private boolean activate;
+
+ FileSourceActivator(boolean activate) {
+ this.activate = activate;
+ }
+
+ @Override
+ public Matcher<View> getConstraints() {
+ return isDisplayed();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Creates the filesource instance on the UI thread";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ assertTrue(Looper.myLooper() == Looper.getMainLooper());
+ if (activate) {
+ fileSource.activate();
+ } else {
+ fileSource.deactivate();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
index 0e4cf6a8ca..2d16291832 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.BackgroundLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,11 +38,8 @@ public class BackgroundLayerTest extends BaseActivityTest {
private void setupLayer() {
Timber.i("Retrieving layer");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- layer = mapboxMap.getLayerAs("background");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ layer = mapboxMap.getLayerAs("background");
});
}
@@ -64,18 +48,15 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -84,16 +65,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-colorTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundColorTransition(options);
- assertEquals(layer.getBackgroundColorTransition(), options);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundColorTransition(options);
+ assertEquals(layer.getBackgroundColorTransition(), options);
});
}
@@ -102,47 +80,12 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(backgroundColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getBackgroundColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
-
- @Test
- public void testBackgroundColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("background-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundColor(
- zoom(
- exponential(
- stop(2, backgroundColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getBackgroundColor());
- assertNotNull(layer.getBackgroundColor().getFunction());
- assertEquals(CameraFunction.class, layer.getBackgroundColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getBackgroundColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getBackgroundColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getBackgroundColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(backgroundColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getBackgroundColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@@ -151,15 +94,12 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(backgroundColor(Color.RED));
- assertEquals(layer.getBackgroundColorAsInt(), Color.RED);
- }
+ // Set and Get
+ layer.setProperties(backgroundColor(Color.RED));
+ assertEquals(layer.getBackgroundColorAsInt(), Color.RED);
});
}
@@ -168,16 +108,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-patternTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundPatternTransition(options);
- assertEquals(layer.getBackgroundPatternTransition(), options);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundPatternTransition(options);
+ assertEquals(layer.getBackgroundPatternTransition(), options);
});
}
@@ -186,46 +123,12 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(backgroundPattern("pedestrian-polygon"));
- assertEquals((String) layer.getBackgroundPattern().getValue(), (String) "pedestrian-polygon");
- }
- });
- }
-
- @Test
- public void testBackgroundPatternAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("background-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundPattern(
- zoom(
- interval(
- stop(2, backgroundPattern("pedestrian-polygon"))
- )
- )
- )
- );
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Verify
- assertNotNull(layer.getBackgroundPattern());
- assertNotNull(layer.getBackgroundPattern().getFunction());
- assertEquals(CameraFunction.class, layer.getBackgroundPattern().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getBackgroundPattern().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getBackgroundPattern().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(backgroundPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getBackgroundPattern().getValue(), (String) "pedestrian-polygon");
});
}
@@ -234,16 +137,13 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-opacityTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.setBackgroundOpacityTransition(options);
- assertEquals(layer.getBackgroundOpacityTransition(), options);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setBackgroundOpacityTransition(options);
+ assertEquals(layer.getBackgroundOpacityTransition(), options);
});
}
@@ -252,48 +152,12 @@ public class BackgroundLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("background-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(backgroundOpacity(0.3f));
- assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f);
- }
- });
- }
-
- @Test
- public void testBackgroundOpacityAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("background-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- backgroundOpacity(
- zoom(
- exponential(
- stop(2, backgroundOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Verify
- assertNotNull(layer.getBackgroundOpacity());
- assertNotNull(layer.getBackgroundOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getBackgroundOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getBackgroundOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getBackgroundOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getBackgroundOpacity().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(backgroundOpacity(0.3f));
+ assertEquals((Float) layer.getBackgroundOpacity().getValue(), (Float) 0.3f);
});
}
-
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
index 559e446307..a851fde6dd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class CircleLayerTest extends BaseActivityTest {
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 CircleLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new CircleLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,237 +72,96 @@ public class CircleLayerTest extends BaseActivityTest {
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);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleRadiusTransition() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-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.setCircleRadiusTransition(options);
- assertEquals(layer.getCircleRadiusTransition(), options);
- }
- });
- }
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
- @Test
- public void testCircleRadiusAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleRadius(0.3f));
- assertEquals((Float) layer.getCircleRadius().getValue(), (Float) 0.3f);
- }
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
@Test
- public void testCircleRadiusAsCameraFunction() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- zoom(
- exponential(
- stop(2, circleRadius(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleRadius());
- assertNotNull(layer.getCircleRadius().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleRadius().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleRadius().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleRadius().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleRadius().getFunction().getStops()).size());
- }
- });
- }
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleRadiusAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleRadius());
- assertNotNull(layer.getCircleRadius().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleRadius().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleRadius().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleRadius().getFunction().getStops().getClass());
- }
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
+
+
@Test
- public void testCircleRadiusAsExponentialSourceFunction() {
+ public void testCircleRadiusTransition() {
validateTestSetup();
setupLayer();
- Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleRadius(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleRadius());
- assertNotNull(layer.getCircleRadius().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleRadius().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleRadius().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleRadius().getFunction().getStops().getClass());
- }
+ Timber.i("circle-radiusTransitionOptions");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleRadiusTransition(options);
+ assertEquals(layer.getCircleRadiusTransition(), options);
});
}
@Test
- public void testCircleRadiusAsCategoricalSourceFunction() {
+ public void testCircleRadiusAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, circleRadius(0.3f))
- )
- ).withDefaultValue(circleRadius(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleRadius());
- assertNotNull(layer.getCircleRadius().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleRadius().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleRadius().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleRadius().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleRadius().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleRadius().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getCircleRadius().getFunction()).getDefaultValue().getValue());
- }
- });
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+ // Set and Get
+ layer.setProperties(circleRadius(0.3f));
+ assertEquals((Float) layer.getCircleRadius().getValue(), (Float) 0.3f);
+ });
}
@Test
- public void testCircleRadiusAsCompositeFunction() {
+ public void testCircleRadiusAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-radius");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleRadius(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, circleRadius(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(circleRadius(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleRadius());
- assertNotNull(layer.getCircleRadius().getFunction());
- assertEquals(CompositeFunction.class, layer.getCircleRadius().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getCircleRadius().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleRadius().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getCircleRadius().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getCircleRadius().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);
- }
+ Timber.i("circle-radius-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(circleRadius(expression));
+ assertEquals(layer.getCircleRadius().getExpression(), expression);
});
}
+
@Test
public void testCircleColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("circle-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.setCircleColorTransition(options);
- assertEquals(layer.getCircleColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleColorTransition(options);
+ assertEquals(layer.getCircleColorTransition(), options);
});
}
@@ -330,157 +170,42 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getCircleColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(
- zoom(
- exponential(
- stop(2, circleColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleColor());
- assertNotNull(layer.getCircleColor().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circleColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getCircleColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testCircleColorAsIdentitySourceFunction() {
+ public void testCircleColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleColor());
- assertNotNull(layer.getCircleColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("circle-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, circleColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleColor());
- assertNotNull(layer.getCircleColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(circleColor(expression));
+ assertEquals(layer.getCircleColor().getExpression(), expression);
});
}
- @Test
- public void testCircleColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", circleColor(Color.RED))
- )
- ).withDefaultValue(circleColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleColor());
- assertNotNull(layer.getCircleColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getCircleColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testCircleColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleColor(Color.RED));
- assertEquals(layer.getCircleColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(circleColor(Color.RED));
+ assertEquals(layer.getCircleColorAsInt(), Color.RED);
});
}
@@ -489,16 +214,13 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blurTransitionOptions");
- 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.setCircleBlurTransition(options);
- assertEquals(layer.getCircleBlurTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleBlurTransition(options);
+ assertEquals(layer.getCircleBlurTransition(), options);
});
}
@@ -507,398 +229,88 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleBlur(0.3f));
- assertEquals((Float) layer.getCircleBlur().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleBlurAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- zoom(
- exponential(
- stop(2, circleBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleBlur());
- assertNotNull(layer.getCircleBlur().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleBlur().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleBlur().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleBlur().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleBlur().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circleBlur(0.3f));
+ assertEquals((Float) layer.getCircleBlur().getValue(), (Float) 0.3f);
});
}
@Test
- public void testCircleBlurAsIdentitySourceFunction() {
+ public void testCircleBlurAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleBlur());
- assertNotNull(layer.getCircleBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleBlur().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleBlur().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("circle-blur-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleBlurAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleBlur());
- assertNotNull(layer.getCircleBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleBlur().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(circleBlur(expression));
+ assertEquals(layer.getCircleBlur().getExpression(), expression);
});
}
- @Test
- public void testCircleBlurAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, circleBlur(0.3f))
- )
- ).withDefaultValue(circleBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleBlur());
- assertNotNull(layer.getCircleBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleBlur().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleBlur().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleBlur().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleBlur().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getCircleBlur().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testCircleBlurAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleBlur(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, circleBlur(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(circleBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleBlur());
- assertNotNull(layer.getCircleBlur().getFunction());
- assertEquals(CompositeFunction.class, layer.getCircleBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getCircleBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleBlur().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getCircleBlur().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getCircleBlur().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 testCircleOpacityTransition() {
validateTestSetup();
setupLayer();
Timber.i("circle-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.setCircleOpacityTransition(options);
- assertEquals(layer.getCircleOpacityTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleOpacityAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleOpacity(0.3f));
- assertEquals((Float) layer.getCircleOpacity().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleOpacityTransition(options);
+ assertEquals(layer.getCircleOpacityTransition(), options);
});
}
@Test
- public void testCircleOpacityAsCameraFunction() {
+ public void testCircleOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- zoom(
- exponential(
- stop(2, circleOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleOpacity());
- assertNotNull(layer.getCircleOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleOpacity().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleOpacityAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleOpacity());
- assertNotNull(layer.getCircleOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(circleOpacity(0.3f));
+ assertEquals((Float) layer.getCircleOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testCircleOpacityAsExponentialSourceFunction() {
+ public void testCircleOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleOpacity());
- assertNotNull(layer.getCircleOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("circle-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleOpacityAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, circleOpacity(0.3f))
- )
- ).withDefaultValue(circleOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleOpacity());
- assertNotNull(layer.getCircleOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getCircleOpacity().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(circleOpacity(expression));
+ assertEquals(layer.getCircleOpacity().getExpression(), expression);
});
-
}
- @Test
- public void testCircleOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, circleOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(circleOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleOpacity());
- assertNotNull(layer.getCircleOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getCircleOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getCircleOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getCircleOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getCircleOpacity().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 testCircleTranslateTransition() {
validateTestSetup();
setupLayer();
Timber.i("circle-translateTransitionOptions");
- 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.setCircleTranslateTransition(options);
- assertEquals(layer.getCircleTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleTranslateTransition(options);
+ assertEquals(layer.getCircleTranslateTransition(), options);
});
}
@@ -907,47 +319,12 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getCircleTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleTranslate(
- zoom(
- exponential(
- stop(2, circleTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleTranslate());
- assertNotNull(layer.getCircleTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circleTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getCircleTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -956,46 +333,12 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getCircleTranslateAnchor().getValue(), (String) CIRCLE_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleTranslateAnchor(
- zoom(
- interval(
- stop(2, circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleTranslateAnchor());
- assertNotNull(layer.getCircleTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getCircleTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getCircleTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circleTranslateAnchor(CIRCLE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getCircleTranslateAnchor().getValue(), (String) CIRCLE_TRANSLATE_ANCHOR_MAP);
});
}
@@ -1004,46 +347,12 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-pitch-scale");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circlePitchScale(CIRCLE_PITCH_SCALE_MAP));
- assertEquals((String) layer.getCirclePitchScale().getValue(), (String) CIRCLE_PITCH_SCALE_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCirclePitchScaleAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-pitch-scale");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circlePitchScale(
- zoom(
- interval(
- stop(2, circlePitchScale(CIRCLE_PITCH_SCALE_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCirclePitchScale());
- assertNotNull(layer.getCirclePitchScale().getFunction());
- assertEquals(CameraFunction.class, layer.getCirclePitchScale().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getCirclePitchScale().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getCirclePitchScale().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circlePitchScale(CIRCLE_PITCH_SCALE_MAP));
+ assertEquals((String) layer.getCirclePitchScale().getValue(), (String) CIRCLE_PITCH_SCALE_MAP);
});
}
@@ -1052,46 +361,12 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP));
- assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCirclePitchAlignmentAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circlePitchAlignment(
- zoom(
- interval(
- stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCirclePitchAlignment());
- assertNotNull(layer.getCirclePitchAlignment().getFunction());
- assertEquals(CameraFunction.class, layer.getCirclePitchAlignment().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getCirclePitchAlignment().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getCirclePitchAlignment().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP);
});
}
@@ -1100,216 +375,58 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-widthTransitionOptions");
- 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.setCircleStrokeWidthTransition(options);
- assertEquals(layer.getCircleStrokeWidthTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeWidthAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleStrokeWidth(0.3f));
- assertEquals((Float) layer.getCircleStrokeWidth().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleStrokeWidthTransition(options);
+ assertEquals(layer.getCircleStrokeWidthTransition(), options);
});
}
@Test
- public void testCircleStrokeWidthAsCameraFunction() {
+ public void testCircleStrokeWidthAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- zoom(
- exponential(
- stop(2, circleStrokeWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeWidth());
- assertNotNull(layer.getCircleStrokeWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleStrokeWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleStrokeWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleStrokeWidth().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeWidthAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeWidth());
- assertNotNull(layer.getCircleStrokeWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleStrokeWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(circleStrokeWidth(0.3f));
+ assertEquals((Float) layer.getCircleStrokeWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testCircleStrokeWidthAsExponentialSourceFunction() {
+ public void testCircleStrokeWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleStrokeWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeWidth());
- assertNotNull(layer.getCircleStrokeWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("circle-stroke-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, circleStrokeWidth(0.3f))
- )
- ).withDefaultValue(circleStrokeWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeWidth());
- assertNotNull(layer.getCircleStrokeWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleStrokeWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getCircleStrokeWidth().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(circleStrokeWidth(expression));
+ assertEquals(layer.getCircleStrokeWidth().getExpression(), expression);
});
-
}
- @Test
- public void testCircleStrokeWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, circleStrokeWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(circleStrokeWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeWidth());
- assertNotNull(layer.getCircleStrokeWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getCircleStrokeWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getCircleStrokeWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getCircleStrokeWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getCircleStrokeWidth().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 testCircleStrokeColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-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.setCircleStrokeColorTransition(options);
- assertEquals(layer.getCircleStrokeColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleStrokeColorTransition(options);
+ assertEquals(layer.getCircleStrokeColorTransition(), options);
});
}
@@ -1318,157 +435,42 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleStrokeColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getCircleStrokeColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(
- zoom(
- exponential(
- stop(2, circleStrokeColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeColor());
- assertNotNull(layer.getCircleStrokeColor().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleStrokeColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleStrokeColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleStrokeColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(circleStrokeColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getCircleStrokeColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testCircleStrokeColorAsIdentitySourceFunction() {
+ public void testCircleStrokeColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeColor());
- assertNotNull(layer.getCircleStrokeColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleStrokeColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("circle-stroke-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, circleStrokeColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeColor());
- assertNotNull(layer.getCircleStrokeColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(circleStrokeColor(expression));
+ assertEquals(layer.getCircleStrokeColor().getExpression(), expression);
});
}
- @Test
- public void testCircleStrokeColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", circleStrokeColor(Color.RED))
- )
- ).withDefaultValue(circleStrokeColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeColor());
- assertNotNull(layer.getCircleStrokeColor().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleStrokeColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleStrokeColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleStrokeColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getCircleStrokeColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testCircleStrokeColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleStrokeColor(Color.RED));
- assertEquals(layer.getCircleStrokeColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(circleStrokeColor(Color.RED));
+ assertEquals(layer.getCircleStrokeColorAsInt(), Color.RED);
});
}
@@ -1477,199 +479,43 @@ public class CircleLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-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.setCircleStrokeOpacityTransition(options);
- assertEquals(layer.getCircleStrokeOpacityTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeOpacityAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(circleStrokeOpacity(0.3f));
- assertEquals((Float) layer.getCircleStrokeOpacity().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setCircleStrokeOpacityTransition(options);
+ assertEquals(layer.getCircleStrokeOpacityTransition(), options);
});
}
@Test
- public void testCircleStrokeOpacityAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- zoom(
- exponential(
- stop(2, circleStrokeOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeOpacity());
- assertNotNull(layer.getCircleStrokeOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getCircleStrokeOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getCircleStrokeOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getCircleStrokeOpacity().getFunction().getStops()).size());
- }
- });
- }
-
- @Test
- public void testCircleStrokeOpacityAsIdentitySourceFunction() {
+ public void testCircleStrokeOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeOpacity());
- assertNotNull(layer.getCircleStrokeOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getCircleStrokeOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeOpacityAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, circleStrokeOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeOpacity());
- assertNotNull(layer.getCircleStrokeOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(circleStrokeOpacity(0.3f));
+ assertEquals((Float) layer.getCircleStrokeOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testCircleStrokeOpacityAsCategoricalSourceFunction() {
+ public void testCircleStrokeOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, circleStrokeOpacity(0.3f))
- )
- ).withDefaultValue(circleStrokeOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeOpacity());
- assertNotNull(layer.getCircleStrokeOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getCircleStrokeOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getCircleStrokeOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getCircleStrokeOpacity().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
+ Timber.i("circle-stroke-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testCircleStrokeOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("circle-stroke-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- circleStrokeOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, circleStrokeOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(circleStrokeOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getCircleStrokeOpacity());
- assertNotNull(layer.getCircleStrokeOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getCircleStrokeOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getCircleStrokeOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getCircleStrokeOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getCircleStrokeOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getCircleStrokeOpacity().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);
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(circleStrokeOpacity(expression));
+ assertEquals(layer.getCircleStrokeOpacity().getExpression(), expression);
});
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java
new file mode 100644
index 0000000000..c4ff79b053
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java
@@ -0,0 +1,207 @@
+package com.mapbox.mapboxsdk.testapp.style;
+
+import android.graphics.Color;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.layers.FillLayer;
+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.activity.BaseActivityTest;
+import com.mapbox.mapboxsdk.testapp.activity.espresso.EspressoTestActivity;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+import timber.log.Timber;
+
+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.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+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.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.toColor;
+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.fillOutlineColor;
+import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
+import static org.junit.Assert.assertEquals;
+
+@RunWith(AndroidJUnit4.class)
+public class ExpressionTest extends BaseActivityTest {
+
+ private FillLayer layer;
+
+ @Test
+ public void testConstantExpressionConversion() {
+ validateTestSetup();
+ setupStyle();
+ Timber.i("camera function");
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // create color expression
+ Expression inputExpression = rgba(255.0f, 0.0f, 0.0f, 1.0f);
+
+ // set color expression
+ layer.setProperties(
+ fillColor(inputExpression)
+ );
+
+ // get color value
+ int color = layer.getFillColor().getColorInt();
+
+ // compare
+ assertEquals("input expression should match", Color.RED, color);
+ });
+ }
+
+ @Test
+ public void testGetExpressionWrapping() {
+ validateTestSetup();
+ setupStyle();
+ Timber.i("camera function");
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // create get expression
+ Expression inputExpression = get("fill");
+
+ // set get expression
+ layer.setProperties(
+ fillColor(inputExpression)
+ );
+
+ // get actual expression
+ Expression actualExpression = layer.getFillColor().getExpression();
+
+ // create wrapped expected expression
+ Expression expectedExpression = toColor(get("fill"));
+
+ // compare
+ assertEquals("input expression should match", expectedExpression, actualExpression);
+ });
+ }
+
+ @Test
+ public void testCameraFunction() {
+ validateTestSetup();
+ setupStyle();
+ Timber.i("camera function");
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // create camera function expression
+ Expression inputExpression = interpolate(
+ exponential(0.5f), zoom(),
+ stop(1.0f, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(5.0f, rgba(0.0f, 0.0f, 255.0f, 1.0f)),
+ stop(10.0f, rgba(0.0f, 255.0f, 0.0f, 1.0f))
+ );
+
+ // set camera function expression
+ layer.setProperties(
+ fillColor(inputExpression)
+ );
+
+ // get camera function expression
+ Expression outputExpression = layer.getFillColor().getExpression();
+
+ // compare
+ assertEquals("input expression should match", inputExpression, outputExpression);
+ });
+ }
+
+ @Test
+ public void testSourceFunction() {
+ validateTestSetup();
+ setupStyle();
+ Timber.i("camera function");
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // create camera function expression
+ Expression inputExpression = toColor(get("fill"));
+
+ // set camera function expression
+ layer.setProperties(
+ fillColor(inputExpression)
+ );
+
+ // get camera function expression
+ Expression outputExpression = layer.getFillColor().getExpression();
+
+ // compare
+ assertEquals("input expression should match", inputExpression, outputExpression);
+ });
+ }
+
+ @Test
+ public void testCompositeFunction() {
+ validateTestSetup();
+ setupStyle();
+ Timber.i("camera function");
+
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // create camera function expression
+ Expression inputExpression = step(zoom(),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(7.0f, match(
+ string(get("name")),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(8.0f, match(
+ string(get("name")),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ ))
+ );
+
+ // set camera function expression
+ layer.setProperties(
+ fillColor(inputExpression)
+ );
+
+ // get camera function expression
+ Expression outputExpression = layer.getFillColor().getExpression();
+
+ // compare
+ assertEquals("input expression should match", inputExpression, outputExpression);
+ });
+ }
+
+ private void setupStyle() {
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ // Add a source
+ Source source;
+ try {
+ source = new GeoJsonSource("amsterdam-parks-source",
+ ResourceUtils.readRawResource(rule.getActivity(), R.raw.amsterdam));
+ mapboxMap.addSource(source);
+ } catch (IOException ioException) {
+ return;
+ }
+
+ // Add a fill layer
+ mapboxMap.addLayer(layer = new FillLayer("amsterdam-parks-layer", source.getId())
+ .withProperties(
+ fillColor(rgba(0.0f, 0.0f, 0.0f, 0.5f)),
+ fillOutlineColor(rgb(0, 0, 255)),
+ fillAntialias(true)
+ )
+ );
+ });
+ }
+
+ @Override
+ protected Class getActivityClass() {
+ return EspressoTestActivity.class;
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
index 66c6093537..f22956072d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
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 FillExtrusionLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new FillExtrusionLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,86 +72,65 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
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);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
@Test
- public void testFillExtrusionOpacityTransition() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("fill-extrusion-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.setFillExtrusionOpacityTransition(options);
- assertEquals(layer.getFillExtrusionOpacityTransition(), options);
- }
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
+
+
@Test
- public void testFillExtrusionOpacityAsConstant() {
+ public void testFillExtrusionOpacityTransition() {
validateTestSetup();
setupLayer();
- Timber.i("fill-extrusion-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionOpacity(0.3f));
- assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f);
- }
+ Timber.i("fill-extrusion-opacityTransitionOptions");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionOpacityTransition(options);
+ assertEquals(layer.getFillExtrusionOpacityTransition(), options);
});
}
@Test
- public void testFillExtrusionOpacityAsCameraFunction() {
+ public void testFillExtrusionOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionOpacity(
- zoom(
- exponential(
- stop(2, fillExtrusionOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionOpacity());
- assertNotNull(layer.getFillExtrusionOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionOpacity().getFunction().getStops()).size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionOpacity(0.3f));
+ assertEquals((Float) layer.getFillExtrusionOpacity().getValue(), (Float) 0.3f);
});
}
@@ -179,16 +139,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-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.setFillExtrusionColorTransition(options);
- assertEquals(layer.getFillExtrusionColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionColorTransition(options);
+ assertEquals(layer.getFillExtrusionColorTransition(), options);
});
}
@@ -197,157 +154,42 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getFillExtrusionColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- zoom(
- exponential(
- stop(2, fillExtrusionColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionColor());
- assertNotNull(layer.getFillExtrusionColor().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillExtrusionColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testFillExtrusionColorAsIdentitySourceFunction() {
+ public void testFillExtrusionColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionColor());
- assertNotNull(layer.getFillExtrusionColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-extrusion-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillExtrusionColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionColor());
- assertNotNull(layer.getFillExtrusionColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(fillExtrusionColor(expression));
+ assertEquals(layer.getFillExtrusionColor().getExpression(), expression);
});
}
- @Test
- public void testFillExtrusionColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillExtrusionColor(Color.RED))
- )
- ).withDefaultValue(fillExtrusionColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionColor());
- assertNotNull(layer.getFillExtrusionColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillExtrusionColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getFillExtrusionColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testFillExtrusionColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionColor(Color.RED));
- assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillExtrusionColor(Color.RED));
+ assertEquals(layer.getFillExtrusionColorAsInt(), Color.RED);
});
}
@@ -356,16 +198,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translateTransitionOptions");
- 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.setFillExtrusionTranslateTransition(options);
- assertEquals(layer.getFillExtrusionTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionTranslateTransition(options);
+ assertEquals(layer.getFillExtrusionTranslateTransition(), options);
});
}
@@ -374,47 +213,12 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionTranslate(
- zoom(
- exponential(
- stop(2, fillExtrusionTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionTranslate());
- assertNotNull(layer.getFillExtrusionTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getFillExtrusionTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -423,46 +227,12 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getFillExtrusionTranslateAnchor().getValue(), (String) FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionTranslateAnchor(
- zoom(
- interval(
- stop(2, fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionTranslateAnchor());
- assertNotNull(layer.getFillExtrusionTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getFillExtrusionTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getFillExtrusionTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionTranslateAnchor(FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillExtrusionTranslateAnchor().getValue(), (String) FILL_EXTRUSION_TRANSLATE_ANCHOR_MAP);
});
}
@@ -471,16 +241,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-patternTransitionOptions");
- 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.setFillExtrusionPatternTransition(options);
- assertEquals(layer.getFillExtrusionPatternTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionPatternTransition(options);
+ assertEquals(layer.getFillExtrusionPatternTransition(), options);
});
}
@@ -489,46 +256,12 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionPattern("pedestrian-polygon"));
- assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionPatternAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionPattern(
- zoom(
- interval(
- stop(2, fillExtrusionPattern("pedestrian-polygon"))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionPattern());
- assertNotNull(layer.getFillExtrusionPattern().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionPattern().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getFillExtrusionPattern().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getFillExtrusionPattern().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillExtrusionPattern().getValue(), (String) "pedestrian-polygon");
});
}
@@ -537,16 +270,13 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-heightTransitionOptions");
- 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.setFillExtrusionHeightTransition(options);
- assertEquals(layer.getFillExtrusionHeightTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionHeightTransition(options);
+ assertEquals(layer.getFillExtrusionHeightTransition(), options);
});
}
@@ -555,198 +285,43 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionHeight(0.3f));
- assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionHeightAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- zoom(
- exponential(
- stop(2, fillExtrusionHeight(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionHeight());
- assertNotNull(layer.getFillExtrusionHeight().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionHeight(0.3f));
+ assertEquals((Float) layer.getFillExtrusionHeight().getValue(), (Float) 0.3f);
});
}
@Test
- public void testFillExtrusionHeightAsIdentitySourceFunction() {
+ public void testFillExtrusionHeightAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionHeight());
- assertNotNull(layer.getFillExtrusionHeight().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-extrusion-height-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionHeightAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillExtrusionHeight(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionHeight());
- assertNotNull(layer.getFillExtrusionHeight().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
- }
- });
- }
-
- @Test
- public void testFillExtrusionHeightAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillExtrusionHeight(0.3f))
- )
- ).withDefaultValue(fillExtrusionHeight(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionHeight());
- assertNotNull(layer.getFillExtrusionHeight().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionHeight().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(fillExtrusionHeight(expression));
+ assertEquals(layer.getFillExtrusionHeight().getExpression(), expression);
});
-
}
- @Test
- public void testFillExtrusionHeightAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionHeight(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, fillExtrusionHeight(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(fillExtrusionHeight(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionHeight());
- assertNotNull(layer.getFillExtrusionHeight().getFunction());
- assertEquals(CompositeFunction.class, layer.getFillExtrusionHeight().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionHeight().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionHeight().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionHeight().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionHeight().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 testFillExtrusionBaseTransition() {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-baseTransitionOptions");
- 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.setFillExtrusionBaseTransition(options);
- assertEquals(layer.getFillExtrusionBaseTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillExtrusionBaseTransition(options);
+ assertEquals(layer.getFillExtrusionBaseTransition(), options);
});
}
@@ -755,181 +330,28 @@ public class FillExtrusionLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillExtrusionBase(0.3f));
- assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionBaseAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- zoom(
- exponential(
- stop(2, fillExtrusionBase(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionBase());
- assertNotNull(layer.getFillExtrusionBase().getFunction());
- assertEquals(CameraFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillExtrusionBase(0.3f));
+ assertEquals((Float) layer.getFillExtrusionBase().getValue(), (Float) 0.3f);
});
}
@Test
- public void testFillExtrusionBaseAsIdentitySourceFunction() {
+ public void testFillExtrusionBaseAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionBase());
- assertNotNull(layer.getFillExtrusionBase().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-extrusion-base-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillExtrusionBaseAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillExtrusionBase(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionBase());
- assertNotNull(layer.getFillExtrusionBase().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
- }
- });
- }
-
- @Test
- public void testFillExtrusionBaseAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillExtrusionBase(0.3f))
- )
- ).withDefaultValue(fillExtrusionBase(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionBase());
- assertNotNull(layer.getFillExtrusionBase().getFunction());
- assertEquals(SourceFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getFillExtrusionBase().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testFillExtrusionBaseAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-extrusion-base");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillExtrusionBase(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, fillExtrusionBase(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(fillExtrusionBase(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillExtrusionBase());
- assertNotNull(layer.getFillExtrusionBase().getFunction());
- assertEquals(CompositeFunction.class, layer.getFillExtrusionBase().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillExtrusionBase().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillExtrusionBase().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getFillExtrusionBase().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillExtrusionBase().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);
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(fillExtrusionBase(expression));
+ assertEquals(layer.getFillExtrusionBase().getExpression(), expression);
});
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
index 939bcb6892..bdbd8958d2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class FillLayerTest extends BaseActivityTest {
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 FillLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new FillLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,67 +72,50 @@ public class FillLayerTest extends BaseActivityTest {
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);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
@Test
- public void testFillAntialiasAsConstant() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("fill-antialias");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillAntialias(true));
- assertEquals((Boolean) layer.getFillAntialias().getValue(), (Boolean) true);
- }
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
+
+
@Test
- public void testFillAntialiasAsCameraFunction() {
+ public void testFillAntialiasAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-antialias");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillAntialias(
- zoom(
- interval(
- stop(2, fillAntialias(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillAntialias());
- assertNotNull(layer.getFillAntialias().getFunction());
- assertEquals(CameraFunction.class, layer.getFillAntialias().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getFillAntialias().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getFillAntialias().getFunction().getStops()).size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillAntialias(true));
+ assertEquals((Boolean) layer.getFillAntialias().getValue(), (Boolean) true);
});
}
@@ -160,216 +124,58 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-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.setFillOpacityTransition(options);
- assertEquals(layer.getFillOpacityTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillOpacityAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillOpacity(0.3f));
- assertEquals((Float) layer.getFillOpacity().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillOpacityTransition(options);
+ assertEquals(layer.getFillOpacityTransition(), options);
});
}
@Test
- public void testFillOpacityAsCameraFunction() {
+ public void testFillOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- zoom(
- exponential(
- stop(2, fillOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOpacity());
- assertNotNull(layer.getFillOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getFillOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillOpacity().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillOpacityAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillOpacity());
- assertNotNull(layer.getFillOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(fillOpacity(0.3f));
+ assertEquals((Float) layer.getFillOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testFillOpacityAsExponentialSourceFunction() {
+ public void testFillOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, fillOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOpacity());
- assertNotNull(layer.getFillOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillOpacityAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, fillOpacity(0.3f))
- )
- ).withDefaultValue(fillOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOpacity());
- assertNotNull(layer.getFillOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getFillOpacity().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(fillOpacity(expression));
+ assertEquals(layer.getFillOpacity().getExpression(), expression);
});
-
}
- @Test
- public void testFillOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, fillOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(fillOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOpacity());
- assertNotNull(layer.getFillOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getFillOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getFillOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getFillOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getFillOpacity().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 testFillColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("fill-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.setFillColorTransition(options);
- assertEquals(layer.getFillColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillColorTransition(options);
+ assertEquals(layer.getFillColorTransition(), options);
});
}
@@ -378,157 +184,42 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getFillColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- zoom(
- exponential(
- stop(2, fillColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillColor());
- assertNotNull(layer.getFillColor().getFunction());
- assertEquals(CameraFunction.class, layer.getFillColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testFillColorAsIdentitySourceFunction() {
+ public void testFillColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillColor());
- assertNotNull(layer.getFillColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillColor());
- assertNotNull(layer.getFillColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(fillColor(expression));
+ assertEquals(layer.getFillColor().getExpression(), expression);
});
}
- @Test
- public void testFillColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillColor(Color.RED))
- )
- ).withDefaultValue(fillColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillColor());
- assertNotNull(layer.getFillColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getFillColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testFillColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillColor(Color.RED));
- assertEquals(layer.getFillColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillColor(Color.RED));
+ assertEquals(layer.getFillColorAsInt(), Color.RED);
});
}
@@ -537,16 +228,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-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.setFillOutlineColorTransition(options);
- assertEquals(layer.getFillOutlineColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillOutlineColorTransition(options);
+ assertEquals(layer.getFillOutlineColorTransition(), options);
});
}
@@ -555,157 +243,42 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillOutlineColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getFillOutlineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillOutlineColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- zoom(
- exponential(
- stop(2, fillOutlineColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOutlineColor());
- assertNotNull(layer.getFillOutlineColor().getFunction());
- assertEquals(CameraFunction.class, layer.getFillOutlineColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillOutlineColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillOutlineColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillOutlineColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillOutlineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getFillOutlineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testFillOutlineColorAsIdentitySourceFunction() {
+ public void testFillOutlineColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getFillOutlineColor());
- assertNotNull(layer.getFillOutlineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOutlineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOutlineColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getFillOutlineColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("fill-outline-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillOutlineColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, fillOutlineColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOutlineColor());
- assertNotNull(layer.getFillOutlineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOutlineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOutlineColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getFillOutlineColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(fillOutlineColor(expression));
+ assertEquals(layer.getFillOutlineColor().getExpression(), expression);
});
}
- @Test
- public void testFillOutlineColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillOutlineColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", fillOutlineColor(Color.RED))
- )
- ).withDefaultValue(fillOutlineColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getFillOutlineColor());
- assertNotNull(layer.getFillOutlineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getFillOutlineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getFillOutlineColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getFillOutlineColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getFillOutlineColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getFillOutlineColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getFillOutlineColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testFillOutlineColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("fill-outline-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillOutlineColor(Color.RED));
- assertEquals(layer.getFillOutlineColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(fillOutlineColor(Color.RED));
+ assertEquals(layer.getFillOutlineColorAsInt(), Color.RED);
});
}
@@ -714,16 +287,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translateTransitionOptions");
- 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.setFillTranslateTransition(options);
- assertEquals(layer.getFillTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillTranslateTransition(options);
+ assertEquals(layer.getFillTranslateTransition(), options);
});
}
@@ -732,47 +302,12 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getFillTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillTranslate(
- zoom(
- exponential(
- stop(2, fillTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillTranslate());
- assertNotNull(layer.getFillTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getFillTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getFillTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getFillTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getFillTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getFillTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -781,46 +316,12 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getFillTranslateAnchor().getValue(), (String) FILL_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillTranslateAnchor(
- zoom(
- interval(
- stop(2, fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillTranslateAnchor());
- assertNotNull(layer.getFillTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getFillTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getFillTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getFillTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getFillTranslateAnchor().getValue(), (String) FILL_TRANSLATE_ANCHOR_MAP);
});
}
@@ -829,16 +330,13 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-patternTransitionOptions");
- 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.setFillPatternTransition(options);
- assertEquals(layer.getFillPatternTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setFillPatternTransition(options);
+ assertEquals(layer.getFillPatternTransition(), options);
});
}
@@ -847,47 +345,12 @@ public class FillLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("fill-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(fillPattern("pedestrian-polygon"));
- assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testFillPatternAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("fill-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- fillPattern(
- zoom(
- interval(
- stop(2, fillPattern("pedestrian-polygon"))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getFillPattern());
- assertNotNull(layer.getFillPattern().getFunction());
- assertEquals(CameraFunction.class, layer.getFillPattern().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getFillPattern().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getFillPattern().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(fillPattern("pedestrian-polygon"));
+ assertEquals((String) layer.getFillPattern().getValue(), (String) "pedestrian-polygon");
});
}
-
-}
+} \ No newline at end of file
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..549f309e4f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java
@@ -0,0 +1,240 @@
+// 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.runner.AndroidJUnit4;
+
+import timber.log.Timber;
+
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.layers.HeatmapLayer;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.*;
+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, (uiController, 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, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
+ });
+ }
+
+ @Test
+ public void testFilter() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
+ });
+ }
+
+
+
+ @Test
+ public void testHeatmapRadiusTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radiusTransitionOptions");
+ invoke(mapboxMap, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapRadius(0.3f));
+ assertEquals((Float) layer.getHeatmapRadius().getValue(), (Float) 0.3f);
+ });
+ }
+
+ @Test
+ public void testHeatmapRadiusAsExpression() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-radius-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(heatmapRadius(expression));
+ assertEquals(layer.getHeatmapRadius().getExpression(), expression);
+ });
+ }
+
+
+ @Test
+ public void testHeatmapWeightAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapWeight(0.3f));
+ assertEquals((Float) layer.getHeatmapWeight().getValue(), (Float) 0.3f);
+ });
+ }
+
+ @Test
+ public void testHeatmapWeightAsExpression() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-weight-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(heatmapWeight(expression));
+ assertEquals(layer.getHeatmapWeight().getExpression(), expression);
+ });
+ }
+
+
+ @Test
+ public void testHeatmapIntensityTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-intensityTransitionOptions");
+ invoke(mapboxMap, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapIntensity(0.3f));
+ assertEquals((Float) layer.getHeatmapIntensity().getValue(), (Float) 0.3f);
+ });
+ }
+
+ @Test
+ public void testHeatmapOpacityTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("heatmap-opacityTransitionOptions");
+ invoke(mapboxMap, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(heatmapOpacity(0.3f));
+ assertEquals((Float) layer.getHeatmapOpacity().getValue(), (Float) 0.3f);
+ });
+ }
+} \ No newline at end of file
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..1fdc6d6dab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java
@@ -0,0 +1,255 @@
+// 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.runner.AndroidJUnit4;
+
+import timber.log.Timber;
+
+import com.mapbox.mapboxsdk.style.expressions.Expression;
+import com.mapbox.mapboxsdk.style.layers.HillshadeLayer;
+import com.mapbox.mapboxsdk.testapp.activity.BaseActivityTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.mapbox.mapboxsdk.style.expressions.Expression.*;
+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, (uiController, 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, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeIlluminationDirection(0.3f));
+ assertEquals((Float) layer.getHillshadeIlluminationDirection().getValue(), (Float) 0.3f);
+ });
+ }
+
+ @Test
+ public void testHillshadeIlluminationAnchorAsConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-illumination-anchor");
+ invoke(mapboxMap, (uiController, 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 testHillshadeExaggerationTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-exaggerationTransitionOptions");
+ invoke(mapboxMap, (uiController, 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, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeExaggeration(0.3f));
+ assertEquals((Float) layer.getHillshadeExaggeration().getValue(), (Float) 0.3f);
+ });
+ }
+
+ @Test
+ public void testHillshadeShadowColorTransition() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-colorTransitionOptions");
+ invoke(mapboxMap, (uiController, 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, (uiController, 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 testHillshadeShadowColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-shadow-color");
+ invoke(mapboxMap, (uiController, 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, (uiController, 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, (uiController, 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 testHillshadeHighlightColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-highlight-color");
+ invoke(mapboxMap, (uiController, 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, (uiController, 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, (uiController, 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 testHillshadeAccentColorAsIntConstant() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("hillshade-accent-color");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(hillshadeAccentColor(Color.RED));
+ assertEquals(layer.getHillshadeAccentColorAsInt(), Color.RED);
+ });
+ }
+} \ No newline at end of file
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/LightTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
index 88da6e45be..52881e2fe6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java
@@ -6,15 +6,12 @@ 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.light.Light;
-import com.mapbox.mapboxsdk.style.functions.Function;
-import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.style.light.Position;
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.FillExtrusionStyleTestActivity;
@@ -27,7 +24,7 @@ import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
@@ -48,14 +45,11 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- light.setAnchor(ANCHOR_MAP);
- assertEquals("Anchor should match", ANCHOR_MAP, light.getAnchor());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ light.setAnchor(ANCHOR_MAP);
+ assertEquals("Anchor should match", ANCHOR_MAP, light.getAnchor());
});
}
@@ -64,15 +58,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("positionTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- light.setPositionTransition(options);
- assertEquals("Transition options should match", options, light.getPositionTransition());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ light.setPositionTransition(options);
+ assertEquals("Transition options should match", options, light.getPositionTransition());
});
}
@@ -81,15 +72,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("position");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- Position position = new Position(1, 2, 3);
- light.setPosition(position);
- assertEquals("Position should match", position, light.getPosition());
- }
+ invoke(mapboxMap,(uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ Position position = new Position(1, 2, 3);
+ light.setPosition(position);
+ assertEquals("Position should match", position, light.getPosition());
});
}
@@ -98,15 +86,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("colorTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- light.setColorTransition(options);
- assertEquals("Transition options should match", options, light.getColorTransition());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ light.setColorTransition(options);
+ assertEquals("Transition options should match", options, light.getColorTransition());
});
}
@@ -115,14 +100,11 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- light.setColor("rgba(0, 0, 0, 1)");
- assertEquals("Color should match", "rgba(0, 0, 0, 1)".replaceAll("\\s+", ""), light.getColor());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ light.setColor("rgba(0, 0, 0, 1)");
+ assertEquals("Color should match", "rgba(0, 0, 0, 1)".replaceAll("\\s+", ""), light.getColor());
});
}
@@ -131,15 +113,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("intensityTransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- light.setIntensityTransition(options);
- assertEquals("Transition options should match", options, light.getIntensityTransition());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ light.setIntensityTransition(options);
+ assertEquals("Transition options should match", options, light.getIntensityTransition());
});
}
@@ -148,14 +127,11 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("intensity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- light.setIntensity(0.3f);
- assertEquals("Intensity should match", 0.3f, light.getIntensity());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ light.setIntensity(0.3f);
+ assertEquals("Intensity should match", 0.3f, light.getIntensity());
});
}
@@ -176,12 +152,12 @@ public class LightTest extends BaseActivityTest {
light = mapboxMap.getLight();
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
- fillExtrusionLayer.setFilter(eq("extrude", "true"));
+ fillExtrusionLayer.setFilter(eq(Expression.get("extrude"), "true"));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
- fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())),
- fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())),
+ fillExtrusionHeight(Expression.get("height")),
+ fillExtrusionBase(Expression.get("min_height")),
fillExtrusionOpacity(0.6f)
);
mapboxMap.addLayer(fillExtrusionLayer);
@@ -193,4 +169,4 @@ public class LightTest extends BaseActivityTest {
protected Class getActivityClass() {
return FillExtrusionStyleTestActivity.class;
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
index 8123d24be8..40cf0f2927 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class LineLayerTest extends BaseActivityTest {
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 LineLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new LineLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,67 +72,50 @@ public class LineLayerTest extends BaseActivityTest {
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);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
@Test
- public void testLineCapAsConstant() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("line-cap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineCap(LINE_CAP_BUTT));
- assertEquals((String) layer.getLineCap().getValue(), (String) LINE_CAP_BUTT);
- }
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
+
+
@Test
- public void testLineCapAsCameraFunction() {
+ public void testLineCapAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-cap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineCap(
- zoom(
- interval(
- stop(2, lineCap(LINE_CAP_BUTT))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineCap());
- assertNotNull(layer.getLineCap().getFunction());
- assertEquals(CameraFunction.class, layer.getLineCap().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLineCap().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLineCap().getFunction().getStops()).size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(lineCap(LINE_CAP_BUTT));
+ assertEquals((String) layer.getLineCap().getValue(), (String) LINE_CAP_BUTT);
});
}
@@ -160,152 +124,42 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-join");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineJoin(LINE_JOIN_BEVEL));
- assertEquals((String) layer.getLineJoin().getValue(), (String) LINE_JOIN_BEVEL);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineJoinAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-join");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineJoin(
- zoom(
- interval(
- stop(2, lineJoin(LINE_JOIN_BEVEL))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineJoin());
- assertNotNull(layer.getLineJoin().getFunction());
- assertEquals(CameraFunction.class, layer.getLineJoin().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLineJoin().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLineJoin().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineJoin(LINE_JOIN_BEVEL));
+ assertEquals((String) layer.getLineJoin().getValue(), (String) LINE_JOIN_BEVEL);
});
}
@Test
- public void testLineJoinAsIdentitySourceFunction() {
+ public void testLineJoinAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-join");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineJoin(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineJoin());
- assertNotNull(layer.getLineJoin().getFunction());
- assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineJoin().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-join-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineJoinAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-join");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineJoin(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, lineJoin(LINE_JOIN_BEVEL))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineJoin());
- assertNotNull(layer.getLineJoin().getFunction());
- assertEquals(SourceFunction.class, layer.getLineJoin().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineJoin().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getLineJoin().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(lineJoin(expression));
+ assertEquals(layer.getLineJoin().getExpression(), expression);
});
}
+
@Test
public void testLineMiterLimitAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-miter-limit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineMiterLimit(0.3f));
- assertEquals((Float) layer.getLineMiterLimit().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineMiterLimitAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-miter-limit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineMiterLimit(
- zoom(
- exponential(
- stop(2, lineMiterLimit(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineMiterLimit());
- assertNotNull(layer.getLineMiterLimit().getFunction());
- assertEquals(CameraFunction.class, layer.getLineMiterLimit().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineMiterLimit().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineMiterLimit().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineMiterLimit().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineMiterLimit(0.3f));
+ assertEquals((Float) layer.getLineMiterLimit().getValue(), (Float) 0.3f);
});
}
@@ -314,47 +168,12 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-round-limit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineRoundLimit(0.3f));
- assertEquals((Float) layer.getLineRoundLimit().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineRoundLimitAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-round-limit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineRoundLimit(
- zoom(
- exponential(
- stop(2, lineRoundLimit(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineRoundLimit());
- assertNotNull(layer.getLineRoundLimit().getFunction());
- assertEquals(CameraFunction.class, layer.getLineRoundLimit().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineRoundLimit().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineRoundLimit().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineRoundLimit().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineRoundLimit(0.3f));
+ assertEquals((Float) layer.getLineRoundLimit().getValue(), (Float) 0.3f);
});
}
@@ -363,216 +182,58 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-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.setLineOpacityTransition(options);
- assertEquals(layer.getLineOpacityTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineOpacityAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineOpacity(0.3f));
- assertEquals((Float) layer.getLineOpacity().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineOpacityTransition(options);
+ assertEquals(layer.getLineOpacityTransition(), options);
});
}
@Test
- public void testLineOpacityAsCameraFunction() {
+ public void testLineOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- zoom(
- exponential(
- stop(2, lineOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOpacity());
- assertNotNull(layer.getLineOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getLineOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineOpacity().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineOpacityAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineOpacity());
- assertNotNull(layer.getLineOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(lineOpacity(0.3f));
+ assertEquals((Float) layer.getLineOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testLineOpacityAsExponentialSourceFunction() {
+ public void testLineOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOpacity());
- assertNotNull(layer.getLineOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineOpacityAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineOpacity(0.3f))
- )
- ).withDefaultValue(lineOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOpacity());
- assertNotNull(layer.getLineOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getLineOpacity().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(lineOpacity(expression));
+ assertEquals(layer.getLineOpacity().getExpression(), expression);
});
-
}
- @Test
- public void testLineOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, lineOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(lineOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOpacity());
- assertNotNull(layer.getLineOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getLineOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getLineOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineOpacity().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 testLineColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("line-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.setLineColorTransition(options);
- assertEquals(layer.getLineColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineColorTransition(options);
+ assertEquals(layer.getLineColorTransition(), options);
});
}
@@ -581,157 +242,42 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getLineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- zoom(
- exponential(
- stop(2, lineColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineColor());
- assertNotNull(layer.getLineColor().getFunction());
- assertEquals(CameraFunction.class, layer.getLineColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getLineColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testLineColorAsIdentitySourceFunction() {
+ public void testLineColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineColor());
- assertNotNull(layer.getLineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getLineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, lineColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineColor());
- assertNotNull(layer.getLineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getLineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(lineColor(expression));
+ assertEquals(layer.getLineColor().getExpression(), expression);
});
}
- @Test
- public void testLineColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", lineColor(Color.RED))
- )
- ).withDefaultValue(lineColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineColor());
- assertNotNull(layer.getLineColor().getFunction());
- assertEquals(SourceFunction.class, layer.getLineColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getLineColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testLineColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineColor(Color.RED));
- assertEquals(layer.getLineColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(lineColor(Color.RED));
+ assertEquals(layer.getLineColorAsInt(), Color.RED);
});
}
@@ -740,16 +286,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translateTransitionOptions");
- 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.setLineTranslateTransition(options);
- assertEquals(layer.getLineTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineTranslateTransition(options);
+ assertEquals(layer.getLineTranslateTransition(), options);
});
}
@@ -758,47 +301,12 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getLineTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineTranslate(
- zoom(
- exponential(
- stop(2, lineTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineTranslate());
- assertNotNull(layer.getLineTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getLineTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getLineTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -807,46 +315,12 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getLineTranslateAnchor().getValue(), (String) LINE_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineTranslateAnchor(
- zoom(
- interval(
- stop(2, lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineTranslateAnchor());
- assertNotNull(layer.getLineTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getLineTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLineTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLineTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineTranslateAnchor(LINE_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getLineTranslateAnchor().getValue(), (String) LINE_TRANSLATE_ANCHOR_MAP);
});
}
@@ -855,16 +329,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-widthTransitionOptions");
- 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.setLineWidthTransition(options);
- assertEquals(layer.getLineWidthTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineWidthTransition(options);
+ assertEquals(layer.getLineWidthTransition(), options);
});
}
@@ -873,198 +344,43 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineWidth(0.3f));
- assertEquals((Float) layer.getLineWidth().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineWidthAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- zoom(
- exponential(
- stop(2, lineWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineWidth());
- assertNotNull(layer.getLineWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getLineWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineWidth(0.3f));
+ assertEquals((Float) layer.getLineWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testLineWidthAsIdentitySourceFunction() {
+ public void testLineWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineWidth());
- assertNotNull(layer.getLineWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineWidthAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineWidth());
- assertNotNull(layer.getLineWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(lineWidth(expression));
+ assertEquals(layer.getLineWidth().getExpression(), expression);
});
}
- @Test
- public void testLineWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineWidth(0.3f))
- )
- ).withDefaultValue(lineWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineWidth());
- assertNotNull(layer.getLineWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testLineWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, lineWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(lineWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineWidth());
- assertNotNull(layer.getLineWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getLineWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineWidth().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 testLineGapWidthTransition() {
validateTestSetup();
setupLayer();
Timber.i("line-gap-widthTransitionOptions");
- 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.setLineGapWidthTransition(options);
- assertEquals(layer.getLineGapWidthTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineGapWidthTransition(options);
+ assertEquals(layer.getLineGapWidthTransition(), options);
});
}
@@ -1073,380 +389,57 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineGapWidth(0.3f));
- assertEquals((Float) layer.getLineGapWidth().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineGapWidthAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- zoom(
- exponential(
- stop(2, lineGapWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineGapWidth());
- assertNotNull(layer.getLineGapWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getLineGapWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineGapWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineGapWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineGapWidth().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineGapWidth(0.3f));
+ assertEquals((Float) layer.getLineGapWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testLineGapWidthAsIdentitySourceFunction() {
+ public void testLineGapWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineGapWidth());
- assertNotNull(layer.getLineGapWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineGapWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineGapWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineGapWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-gap-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineGapWidthAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineGapWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineGapWidth());
- assertNotNull(layer.getLineGapWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineGapWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineGapWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineGapWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(lineGapWidth(expression));
+ assertEquals(layer.getLineGapWidth().getExpression(), expression);
});
}
- @Test
- public void testLineGapWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineGapWidth(0.3f))
- )
- ).withDefaultValue(lineGapWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineGapWidth());
- assertNotNull(layer.getLineGapWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getLineGapWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineGapWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineGapWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineGapWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineGapWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getLineGapWidth().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testLineGapWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-gap-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineGapWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, lineGapWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(lineGapWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineGapWidth());
- assertNotNull(layer.getLineGapWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getLineGapWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineGapWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineGapWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getLineGapWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineGapWidth().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 testLineOffsetTransition() {
validateTestSetup();
setupLayer();
Timber.i("line-offsetTransitionOptions");
- 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.setLineOffsetTransition(options);
- assertEquals(layer.getLineOffsetTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineOffsetAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineOffset(0.3f));
- assertEquals((Float) layer.getLineOffset().getValue(), (Float) 0.3f);
- }
- });
- }
-
- @Test
- public void testLineOffsetAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- zoom(
- exponential(
- stop(2, lineOffset(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOffset());
- assertNotNull(layer.getLineOffset().getFunction());
- assertEquals(CameraFunction.class, layer.getLineOffset().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineOffset().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineOffset().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineOffset().getFunction().getStops()).size());
- }
- });
- }
-
- @Test
- public void testLineOffsetAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineOffset());
- assertNotNull(layer.getLineOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOffset().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineOffset().getFunction().getStops().getClass());
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineOffsetTransition(options);
+ assertEquals(layer.getLineOffsetTransition(), options);
});
}
@Test
- public void testLineOffsetAsExponentialSourceFunction() {
+ public void testLineOffsetAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineOffset(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOffset());
- assertNotNull(layer.getLineOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOffset().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineOffset().getFunction().getStops().getClass());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineOffsetAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineOffset(0.3f))
- )
- ).withDefaultValue(lineOffset(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOffset());
- assertNotNull(layer.getLineOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getLineOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineOffset().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineOffset().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineOffset().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineOffset().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getLineOffset().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testLineOffsetAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineOffset(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, lineOffset(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(lineOffset(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineOffset());
- assertNotNull(layer.getLineOffset().getFunction());
- assertEquals(CompositeFunction.class, layer.getLineOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineOffset().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineOffset().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getLineOffset().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineOffset().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);
- }
+ // Set and Get
+ layer.setProperties(lineOffset(0.3f));
+ assertEquals((Float) layer.getLineOffset().getValue(), (Float) 0.3f);
});
}
@@ -1455,216 +448,58 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-blurTransitionOptions");
- 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.setLineBlurTransition(options);
- assertEquals(layer.getLineBlurTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineBlurAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineBlur(0.3f));
- assertEquals((Float) layer.getLineBlur().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineBlurTransition(options);
+ assertEquals(layer.getLineBlurTransition(), options);
});
}
@Test
- public void testLineBlurAsCameraFunction() {
+ public void testLineBlurAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- zoom(
- exponential(
- stop(2, lineBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineBlur());
- assertNotNull(layer.getLineBlur().getFunction());
- assertEquals(CameraFunction.class, layer.getLineBlur().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getLineBlur().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getLineBlur().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getLineBlur().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineBlurAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getLineBlur());
- assertNotNull(layer.getLineBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getLineBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineBlur().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getLineBlur().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(lineBlur(0.3f));
+ assertEquals((Float) layer.getLineBlur().getValue(), (Float) 0.3f);
});
}
@Test
- public void testLineBlurAsExponentialSourceFunction() {
+ public void testLineBlurAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, lineBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineBlur());
- assertNotNull(layer.getLineBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getLineBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineBlur().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("line-blur-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineBlurAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, lineBlur(0.3f))
- )
- ).withDefaultValue(lineBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineBlur());
- assertNotNull(layer.getLineBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getLineBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineBlur().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getLineBlur().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getLineBlur().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getLineBlur().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getLineBlur().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(lineBlur(expression));
+ assertEquals(layer.getLineBlur().getExpression(), expression);
});
-
}
- @Test
- public void testLineBlurAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineBlur(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, lineBlur(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(lineBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getLineBlur());
- assertNotNull(layer.getLineBlur().getFunction());
- assertEquals(CompositeFunction.class, layer.getLineBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getLineBlur().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getLineBlur().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineBlur().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 testLineDasharrayTransition() {
validateTestSetup();
setupLayer();
Timber.i("line-dasharrayTransitionOptions");
- 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.setLineDasharrayTransition(options);
- assertEquals(layer.getLineDasharrayTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLineDasharrayTransition(options);
+ assertEquals(layer.getLineDasharrayTransition(), options);
});
}
@@ -1673,46 +508,12 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-dasharray");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(lineDasharray(new Float[] {}));
- assertEquals((Float[]) layer.getLineDasharray().getValue(), (Float[]) new Float[] {});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLineDasharrayAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-dasharray");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- lineDasharray(
- zoom(
- interval(
- stop(2, lineDasharray(new Float[] {}))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLineDasharray());
- assertNotNull(layer.getLineDasharray().getFunction());
- assertEquals(CameraFunction.class, layer.getLineDasharray().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLineDasharray().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLineDasharray().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(lineDasharray(new Float[] {}));
+ assertEquals((Float[]) layer.getLineDasharray().getValue(), (Float[]) new Float[] {});
});
}
@@ -1721,16 +522,13 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-patternTransitionOptions");
- 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.setLinePatternTransition(options);
- assertEquals(layer.getLinePatternTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setLinePatternTransition(options);
+ assertEquals(layer.getLinePatternTransition(), options);
});
}
@@ -1739,47 +537,12 @@ public class LineLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("line-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(linePattern("pedestrian-polygon"));
- assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testLinePatternAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("line-pattern");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- linePattern(
- zoom(
- interval(
- stop(2, linePattern("pedestrian-polygon"))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getLinePattern());
- assertNotNull(layer.getLinePattern().getFunction());
- assertEquals(CameraFunction.class, layer.getLinePattern().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getLinePattern().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getLinePattern().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(linePattern("pedestrian-polygon"));
+ assertEquals((String) layer.getLinePattern().getValue(), (String) "pedestrian-polygon");
});
}
-
-}
+} \ No newline at end of file
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..0410d09369 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
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.RasterLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class RasterLayerTest extends BaseActivityTest {
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 RasterLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new RasterLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,16 +72,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-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.setRasterOpacityTransition(options);
- assertEquals(layer.getRasterOpacityTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterOpacityTransition(options);
+ assertEquals(layer.getRasterOpacityTransition(), options);
});
}
@@ -109,47 +87,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterOpacity(0.3f));
- assertEquals((Float) layer.getRasterOpacity().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterOpacityAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterOpacity(
- zoom(
- exponential(
- stop(2, rasterOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterOpacity());
- assertNotNull(layer.getRasterOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterOpacity().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterOpacity(0.3f));
+ assertEquals((Float) layer.getRasterOpacity().getValue(), (Float) 0.3f);
});
}
@@ -158,16 +101,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-hue-rotateTransitionOptions");
- 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.setRasterHueRotateTransition(options);
- assertEquals(layer.getRasterHueRotateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterHueRotateTransition(options);
+ assertEquals(layer.getRasterHueRotateTransition(), options);
});
}
@@ -176,47 +116,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-hue-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterHueRotate(0.3f));
- assertEquals((Float) layer.getRasterHueRotate().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterHueRotateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-hue-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterHueRotate(
- zoom(
- exponential(
- stop(2, rasterHueRotate(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterHueRotate());
- assertNotNull(layer.getRasterHueRotate().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterHueRotate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterHueRotate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterHueRotate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterHueRotate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterHueRotate(0.3f));
+ assertEquals((Float) layer.getRasterHueRotate().getValue(), (Float) 0.3f);
});
}
@@ -225,16 +130,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-minTransitionOptions");
- 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.setRasterBrightnessMinTransition(options);
- assertEquals(layer.getRasterBrightnessMinTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterBrightnessMinTransition(options);
+ assertEquals(layer.getRasterBrightnessMinTransition(), options);
});
}
@@ -243,47 +145,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-min");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterBrightnessMin(0.3f));
- assertEquals((Float) layer.getRasterBrightnessMin().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterBrightnessMinAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-brightness-min");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterBrightnessMin(
- zoom(
- exponential(
- stop(2, rasterBrightnessMin(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterBrightnessMin());
- assertNotNull(layer.getRasterBrightnessMin().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterBrightnessMin().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterBrightnessMin().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterBrightnessMin().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterBrightnessMin().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterBrightnessMin(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMin().getValue(), (Float) 0.3f);
});
}
@@ -292,16 +159,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-maxTransitionOptions");
- 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.setRasterBrightnessMaxTransition(options);
- assertEquals(layer.getRasterBrightnessMaxTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterBrightnessMaxTransition(options);
+ assertEquals(layer.getRasterBrightnessMaxTransition(), options);
});
}
@@ -310,47 +174,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-brightness-max");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterBrightnessMax(0.3f));
- assertEquals((Float) layer.getRasterBrightnessMax().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterBrightnessMaxAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-brightness-max");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterBrightnessMax(
- zoom(
- exponential(
- stop(2, rasterBrightnessMax(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterBrightnessMax());
- assertNotNull(layer.getRasterBrightnessMax().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterBrightnessMax().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterBrightnessMax().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterBrightnessMax().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterBrightnessMax().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterBrightnessMax(0.3f));
+ assertEquals((Float) layer.getRasterBrightnessMax().getValue(), (Float) 0.3f);
});
}
@@ -359,16 +188,13 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-saturationTransitionOptions");
- 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.setRasterSaturationTransition(options);
- assertEquals(layer.getRasterSaturationTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterSaturationTransition(options);
+ assertEquals(layer.getRasterSaturationTransition(), options);
});
}
@@ -377,47 +203,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-saturation");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterSaturation(0.3f));
- assertEquals((Float) layer.getRasterSaturation().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterSaturationAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-saturation");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterSaturation(
- zoom(
- exponential(
- stop(2, rasterSaturation(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterSaturation());
- assertNotNull(layer.getRasterSaturation().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterSaturation().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterSaturation().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterSaturation().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterSaturation().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterSaturation(0.3f));
+ assertEquals((Float) layer.getRasterSaturation().getValue(), (Float) 0.3f);
});
}
@@ -426,83 +217,27 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-contrastTransitionOptions");
- 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.setRasterContrastTransition(options);
- assertEquals(layer.getRasterContrastTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterContrastAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-contrast");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterContrast(0.3f));
- assertEquals((Float) layer.getRasterContrast().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setRasterContrastTransition(options);
+ assertEquals(layer.getRasterContrastTransition(), options);
});
}
@Test
- public void testRasterContrastAsCameraFunction() {
+ public void testRasterContrastAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("raster-contrast");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterContrast(
- zoom(
- exponential(
- stop(2, rasterContrast(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterContrast());
- assertNotNull(layer.getRasterContrast().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterContrast().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterContrast().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterContrast().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterContrast().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @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);
- }
+ // Set and Get
+ layer.setProperties(rasterContrast(0.3f));
+ assertEquals((Float) layer.getRasterContrast().getValue(), (Float) 0.3f);
});
}
@@ -511,48 +246,12 @@ public class RasterLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("raster-fade-duration");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(rasterFadeDuration(0.3f));
- assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testRasterFadeDurationAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("raster-fade-duration");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- rasterFadeDuration(
- zoom(
- exponential(
- stop(2, rasterFadeDuration(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getRasterFadeDuration());
- assertNotNull(layer.getRasterFadeDuration().getFunction());
- assertEquals(CameraFunction.class, layer.getRasterFadeDuration().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getRasterFadeDuration().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getRasterFadeDuration().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getRasterFadeDuration().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(rasterFadeDuration(0.3f));
+ assertEquals((Float) layer.getRasterFadeDuration().getValue(), (Float) 0.3f);
});
}
-
-}
+} \ No newline at end of file
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..23a75d1642 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());
});
}
@@ -294,6 +280,20 @@ public class RuntimeStyleTests extends BaseActivityTest {
});
}
+ @Test
+ public void testRemoveNonExistingSource() {
+ invoke(mapboxMap, (uiController, mapboxMap) -> mapboxMap.removeSource("source"));
+ }
+
+ @Test
+ public void testRemoveNonExistingLayer() {
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ mapboxMap.removeLayer("layer");
+ mapboxMap.removeLayerAt(mapboxMap.getLayers().size() + 1);
+ mapboxMap.removeLayerAt(-1);
+ });
+ }
+
/**
* https://github.com/mapbox/mapbox-gl-native/issues/7973
*/
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..62f73b1507 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java
@@ -3,31 +3,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -51,17 +38,14 @@ public class SymbolLayerTest extends BaseActivityTest {
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 SymbolLayer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new SymbolLayer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
}
@@ -71,18 +55,15 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
@@ -91,67 +72,50 @@ public class SymbolLayerTest extends BaseActivityTest {
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);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
+
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
@Test
- public void testSymbolPlacementAsConstant() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("symbol-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
- assertEquals((String) layer.getSymbolPlacement().getValue(), (String) SYMBOL_PLACEMENT_POINT);
- }
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
+
+ // Get initial
+ assertEquals(layer.getFilter(), null);
+
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
+
+
@Test
- public void testSymbolPlacementAsCameraFunction() {
+ public void testSymbolPlacementAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("symbol-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolPlacement(
- zoom(
- interval(
- stop(2, symbolPlacement(SYMBOL_PLACEMENT_POINT))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getSymbolPlacement());
- assertNotNull(layer.getSymbolPlacement().getFunction());
- assertEquals(CameraFunction.class, layer.getSymbolPlacement().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getSymbolPlacement().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getSymbolPlacement().getFunction().getStops()).size());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT));
+ assertEquals((String) layer.getSymbolPlacement().getValue(), (String) SYMBOL_PLACEMENT_POINT);
});
}
@@ -160,47 +124,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(symbolSpacing(0.3f));
- assertEquals((Float) layer.getSymbolSpacing().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testSymbolSpacingAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("symbol-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolSpacing(
- zoom(
- exponential(
- stop(2, symbolSpacing(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getSymbolSpacing());
- assertNotNull(layer.getSymbolSpacing().getFunction());
- assertEquals(CameraFunction.class, layer.getSymbolSpacing().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getSymbolSpacing().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getSymbolSpacing().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getSymbolSpacing().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(symbolSpacing(0.3f));
+ assertEquals((Float) layer.getSymbolSpacing().getValue(), (Float) 0.3f);
});
}
@@ -209,46 +138,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("symbol-avoid-edges");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(symbolAvoidEdges(true));
- assertEquals((Boolean) layer.getSymbolAvoidEdges().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testSymbolAvoidEdgesAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("symbol-avoid-edges");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- symbolAvoidEdges(
- zoom(
- interval(
- stop(2, symbolAvoidEdges(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getSymbolAvoidEdges());
- assertNotNull(layer.getSymbolAvoidEdges().getFunction());
- assertEquals(CameraFunction.class, layer.getSymbolAvoidEdges().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getSymbolAvoidEdges().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getSymbolAvoidEdges().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(symbolAvoidEdges(true));
+ assertEquals((Boolean) layer.getSymbolAvoidEdges().getValue(), (Boolean) true);
});
}
@@ -257,46 +152,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-allow-overlap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconAllowOverlap(true));
- assertEquals((Boolean) layer.getIconAllowOverlap().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconAllowOverlapAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-allow-overlap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconAllowOverlap(
- zoom(
- interval(
- stop(2, iconAllowOverlap(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconAllowOverlap());
- assertNotNull(layer.getIconAllowOverlap().getFunction());
- assertEquals(CameraFunction.class, layer.getIconAllowOverlap().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconAllowOverlap().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconAllowOverlap().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconAllowOverlap(true));
+ assertEquals((Boolean) layer.getIconAllowOverlap().getValue(), (Boolean) true);
});
}
@@ -305,46 +166,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-ignore-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconIgnorePlacement(true));
- assertEquals((Boolean) layer.getIconIgnorePlacement().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconIgnorePlacementAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-ignore-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconIgnorePlacement(
- zoom(
- interval(
- stop(2, iconIgnorePlacement(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconIgnorePlacement());
- assertNotNull(layer.getIconIgnorePlacement().getFunction());
- assertEquals(CameraFunction.class, layer.getIconIgnorePlacement().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconIgnorePlacement().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconIgnorePlacement().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconIgnorePlacement(true));
+ assertEquals((Boolean) layer.getIconIgnorePlacement().getValue(), (Boolean) true);
});
}
@@ -353,46 +180,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-optional");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconOptional(true));
- assertEquals((Boolean) layer.getIconOptional().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconOptionalAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-optional");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOptional(
- zoom(
- interval(
- stop(2, iconOptional(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOptional());
- assertNotNull(layer.getIconOptional().getFunction());
- assertEquals(CameraFunction.class, layer.getIconOptional().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconOptional().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconOptional().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconOptional(true));
+ assertEquals((Boolean) layer.getIconOptional().getValue(), (Boolean) true);
});
}
@@ -401,46 +194,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-rotation-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP));
- assertEquals((String) layer.getIconRotationAlignment().getValue(), (String) ICON_ROTATION_ALIGNMENT_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconRotationAlignmentAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-rotation-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotationAlignment(
- zoom(
- interval(
- stop(2, iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconRotationAlignment());
- assertNotNull(layer.getIconRotationAlignment().getFunction());
- assertEquals(CameraFunction.class, layer.getIconRotationAlignment().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconRotationAlignment().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconRotationAlignment().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconRotationAlignment().getValue(), (String) ICON_ROTATION_ALIGNMENT_MAP);
});
}
@@ -449,228 +208,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconSize(0.3f));
- assertEquals((Float) layer.getIconSize().getValue(), (Float) 0.3f);
- }
- });
- }
-
- @Test
- public void testIconSizeAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- zoom(
- exponential(
- stop(2, iconSize(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconSize());
- assertNotNull(layer.getIconSize().getFunction());
- assertEquals(CameraFunction.class, layer.getIconSize().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconSize().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconSize().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconSize().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconSizeAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconSize());
- assertNotNull(layer.getIconSize().getFunction());
- assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconSize().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(iconSize(0.3f));
+ assertEquals((Float) layer.getIconSize().getValue(), (Float) 0.3f);
});
}
@Test
- public void testIconSizeAsExponentialSourceFunction() {
+ public void testIconSizeAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconSize(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconSize());
- assertNotNull(layer.getIconSize().getFunction());
- assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconSize().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-size-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconSizeAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, iconSize(0.3f))
- )
- ).withDefaultValue(iconSize(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconSize());
- assertNotNull(layer.getIconSize().getFunction());
- assertEquals(SourceFunction.class, layer.getIconSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconSize().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconSize().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getIconSize().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(iconSize(expression));
+ assertEquals(layer.getIconSize().getExpression(), expression);
});
-
}
- @Test
- public void testIconSizeAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconSize(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, iconSize(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(iconSize(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconSize());
- assertNotNull(layer.getIconSize().getFunction());
- assertEquals(CompositeFunction.class, layer.getIconSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconSize().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconSize().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getIconSize().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconSize().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 testIconTextFitAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconTextFit(ICON_TEXT_FIT_NONE));
- assertEquals((String) layer.getIconTextFit().getValue(), (String) ICON_TEXT_FIT_NONE);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconTextFitAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-text-fit");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTextFit(
- zoom(
- interval(
- stop(2, iconTextFit(ICON_TEXT_FIT_NONE))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconTextFit());
- assertNotNull(layer.getIconTextFit().getFunction());
- assertEquals(CameraFunction.class, layer.getIconTextFit().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconTextFit().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconTextFit().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconTextFit(ICON_TEXT_FIT_NONE));
+ assertEquals((String) layer.getIconTextFit().getValue(), (String) ICON_TEXT_FIT_NONE);
});
}
@@ -679,47 +252,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-text-fit-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconTextFitPadding(new Float[] {0f, 0f, 0f, 0f}));
- assertEquals((Float[]) layer.getIconTextFitPadding().getValue(), (Float[]) new Float[] {0f, 0f, 0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconTextFitPaddingAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-text-fit-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTextFitPadding(
- zoom(
- exponential(
- stop(2, iconTextFitPadding(new Float[] {0f, 0f, 0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconTextFitPadding());
- assertNotNull(layer.getIconTextFitPadding().getFunction());
- assertEquals(CameraFunction.class, layer.getIconTextFitPadding().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconTextFitPadding().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconTextFitPadding().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconTextFitPadding().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconTextFitPadding(new Float[] {0f, 0f, 0f, 0f}));
+ assertEquals((Float[]) layer.getIconTextFitPadding().getValue(), (Float[]) new Float[] {0f, 0f, 0f, 0f});
});
}
@@ -728,334 +266,72 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-image");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconImage("undefined"));
- assertEquals((String) layer.getIconImage().getValue(), (String) "undefined");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconImageAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-image");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconImage(
- zoom(
- interval(
- stop(2, iconImage("undefined"))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconImage());
- assertNotNull(layer.getIconImage().getFunction());
- assertEquals(CameraFunction.class, layer.getIconImage().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconImage().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconImage().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconImage("undefined"));
+ assertEquals((String) layer.getIconImage().getValue(), (String) "undefined");
});
}
@Test
- public void testIconImageAsIdentitySourceFunction() {
+ public void testIconImageAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-image");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconImage(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconImage());
- assertNotNull(layer.getIconImage().getFunction());
- assertEquals(SourceFunction.class, layer.getIconImage().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconImage().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconImage().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-image-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconImageAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-image");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconImage(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, iconImage("undefined"))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconImage());
- assertNotNull(layer.getIconImage().getFunction());
- assertEquals(SourceFunction.class, layer.getIconImage().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconImage().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getIconImage().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(iconImage(expression));
+ assertEquals(layer.getIconImage().getExpression(), expression);
});
}
- @Test
- public void testIconRotateAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconRotate(0.3f));
- assertEquals((Float) layer.getIconRotate().getValue(), (Float) 0.3f);
- }
- });
- }
@Test
- public void testIconRotateAsCameraFunction() {
+ public void testIconRotateAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- zoom(
- exponential(
- stop(2, iconRotate(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconRotate());
- assertNotNull(layer.getIconRotate().getFunction());
- assertEquals(CameraFunction.class, layer.getIconRotate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconRotate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconRotate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconRotate().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconRotateAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconRotate());
- assertNotNull(layer.getIconRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getIconRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconRotate().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconRotate().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(iconRotate(0.3f));
+ assertEquals((Float) layer.getIconRotate().getValue(), (Float) 0.3f);
});
}
@Test
- public void testIconRotateAsExponentialSourceFunction() {
+ public void testIconRotateAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconRotate(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconRotate());
- assertNotNull(layer.getIconRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getIconRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconRotate().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconRotate().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-rotate-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconRotateAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, iconRotate(0.3f))
- )
- ).withDefaultValue(iconRotate(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconRotate());
- assertNotNull(layer.getIconRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getIconRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconRotate().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconRotate().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconRotate().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconRotate().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getIconRotate().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(iconRotate(expression));
+ assertEquals(layer.getIconRotate().getExpression(), expression);
});
-
}
- @Test
- public void testIconRotateAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconRotate(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, iconRotate(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(iconRotate(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconRotate());
- assertNotNull(layer.getIconRotate().getFunction());
- assertEquals(CompositeFunction.class, layer.getIconRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconRotate().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconRotate().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getIconRotate().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconRotate().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 testIconPaddingAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconPadding(0.3f));
- assertEquals((Float) layer.getIconPadding().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconPaddingAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconPadding(
- zoom(
- exponential(
- stop(2, iconPadding(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconPadding());
- assertNotNull(layer.getIconPadding().getFunction());
- assertEquals(CameraFunction.class, layer.getIconPadding().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconPadding().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconPadding().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconPadding().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconPadding(0.3f));
+ assertEquals((Float) layer.getIconPadding().getValue(), (Float) 0.3f);
});
}
@@ -1064,46 +340,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-keep-upright");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconKeepUpright(true));
- assertEquals((Boolean) layer.getIconKeepUpright().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconKeepUprightAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-keep-upright");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconKeepUpright(
- zoom(
- interval(
- stop(2, iconKeepUpright(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconKeepUpright());
- assertNotNull(layer.getIconKeepUpright().getFunction());
- assertEquals(CameraFunction.class, layer.getIconKeepUpright().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconKeepUpright().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconKeepUpright().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconKeepUpright(true));
+ assertEquals((Boolean) layer.getIconKeepUpright().getValue(), (Boolean) true);
});
}
@@ -1112,104 +354,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconOffset(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getIconOffset().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
-
- @Test
- public void testIconOffsetAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOffset(
- zoom(
- exponential(
- stop(2, iconOffset(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOffset());
- assertNotNull(layer.getIconOffset().getFunction());
- assertEquals(CameraFunction.class, layer.getIconOffset().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconOffset().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconOffset().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconOffset().getFunction().getStops()).size());
- }
- });
- }
-
- @Test
- public void testIconOffsetAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOffset(property("FeaturePropertyA", Stops.<Float[]>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconOffset());
- assertNotNull(layer.getIconOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getIconOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOffset().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconOffset().getFunction().getStops().getClass());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconOffsetAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOffset(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, iconOffset(new Float[] {0f, 0f}))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOffset());
- assertNotNull(layer.getIconOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getIconOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOffset().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getIconOffset().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(iconOffset(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getIconOffset().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -1218,151 +368,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER));
- assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconAnchor(
- zoom(
- interval(
- stop(2, iconAnchor(ICON_ANCHOR_CENTER))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconAnchor());
- assertNotNull(layer.getIconAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getIconAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconAnchor(ICON_ANCHOR_CENTER));
+ assertEquals((String) layer.getIconAnchor().getValue(), (String) ICON_ANCHOR_CENTER);
});
}
@Test
- public void testIconAnchorAsIdentitySourceFunction() {
+ public void testIconAnchorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconAnchor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconAnchor());
- assertNotNull(layer.getIconAnchor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-anchor-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconAnchorAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconAnchor(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, iconAnchor(ICON_ANCHOR_CENTER))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconAnchor());
- assertNotNull(layer.getIconAnchor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconAnchor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconAnchor().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getIconAnchor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(iconAnchor(expression));
+ assertEquals(layer.getIconAnchor().getExpression(), expression);
});
}
+
@Test
public void testIconPitchAlignmentAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP));
- assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconPitchAlignmentAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconPitchAlignment(
- zoom(
- interval(
- stop(2, iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconPitchAlignment());
- assertNotNull(layer.getIconPitchAlignment().getFunction());
- assertEquals(CameraFunction.class, layer.getIconPitchAlignment().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconPitchAlignment().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconPitchAlignment().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP);
});
}
@@ -1371,46 +412,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP));
- assertEquals((String) layer.getTextPitchAlignment().getValue(), (String) TEXT_PITCH_ALIGNMENT_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextPitchAlignmentAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-pitch-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textPitchAlignment(
- zoom(
- interval(
- stop(2, textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextPitchAlignment());
- assertNotNull(layer.getTextPitchAlignment().getFunction());
- assertEquals(CameraFunction.class, layer.getTextPitchAlignment().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextPitchAlignment().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextPitchAlignment().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textPitchAlignment(TEXT_PITCH_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextPitchAlignment().getValue(), (String) TEXT_PITCH_ALIGNMENT_MAP);
});
}
@@ -1419,46 +426,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotation-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP));
- assertEquals((String) layer.getTextRotationAlignment().getValue(), (String) TEXT_ROTATION_ALIGNMENT_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextRotationAlignmentAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-rotation-alignment");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotationAlignment(
- zoom(
- interval(
- stop(2, textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextRotationAlignment());
- assertNotNull(layer.getTextRotationAlignment().getFunction());
- assertEquals(CameraFunction.class, layer.getTextRotationAlignment().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextRotationAlignment().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextRotationAlignment().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textRotationAlignment(TEXT_ROTATION_ALIGNMENT_MAP));
+ assertEquals((String) layer.getTextRotationAlignment().getValue(), (String) TEXT_ROTATION_ALIGNMENT_MAP);
});
}
@@ -1467,151 +440,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-field");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textField(""));
- assertEquals((String) layer.getTextField().getValue(), (String) "");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextFieldAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-field");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textField(
- zoom(
- interval(
- stop(2, textField(""))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextField());
- assertNotNull(layer.getTextField().getFunction());
- assertEquals(CameraFunction.class, layer.getTextField().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextField().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextField().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textField(""));
+ assertEquals((String) layer.getTextField().getValue(), (String) "");
});
}
@Test
- public void testTextFieldAsIdentitySourceFunction() {
+ public void testTextFieldAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-field");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textField(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextField());
- assertNotNull(layer.getTextField().getFunction());
- assertEquals(SourceFunction.class, layer.getTextField().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextField().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextField().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-field-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextFieldAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-field");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textField(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textField(""))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextField());
- assertNotNull(layer.getTextField().getFunction());
- assertEquals(SourceFunction.class, layer.getTextField().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextField().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getTextField().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(textField(expression));
+ assertEquals(layer.getTextField().getExpression(), expression);
});
}
+
@Test
public void testTextFontAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-font");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}));
- assertEquals((String[]) layer.getTextFont().getValue(), (String[]) new String[]{"Open Sans Regular", "Arial Unicode MS Regular"});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextFontAsCameraFunction() {
- 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(
- zoom(
- interval(
- stop(2, textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextFont());
- assertNotNull(layer.getTextFont().getFunction());
- assertEquals(CameraFunction.class, layer.getTextFont().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextFont().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextFont().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}));
+ assertEquals((String[]) layer.getTextFont().getValue(), (String[]) new String[]{"Open Sans Regular", "Arial Unicode MS Regular"});
});
}
@@ -1620,411 +484,72 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textSize(0.3f));
- assertEquals((Float) layer.getTextSize().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextSizeAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- zoom(
- exponential(
- stop(2, textSize(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextSize());
- assertNotNull(layer.getTextSize().getFunction());
- assertEquals(CameraFunction.class, layer.getTextSize().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextSize().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextSize().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextSize().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textSize(0.3f));
+ assertEquals((Float) layer.getTextSize().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextSizeAsIdentitySourceFunction() {
+ public void testTextSizeAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextSize());
- assertNotNull(layer.getTextSize().getFunction());
- assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextSize().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-size-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextSizeAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textSize(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextSize());
- assertNotNull(layer.getTextSize().getFunction());
- assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextSize().getFunction().getStops().getClass());
- }
- });
- }
-
- @Test
- public void testTextSizeAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textSize(0.3f))
- )
- ).withDefaultValue(textSize(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextSize());
- assertNotNull(layer.getTextSize().getFunction());
- assertEquals(SourceFunction.class, layer.getTextSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextSize().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextSize().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextSize().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textSize(expression));
+ assertEquals(layer.getTextSize().getExpression(), expression);
});
-
}
- @Test
- public void testTextSizeAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-size");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textSize(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textSize(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textSize(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextSize());
- assertNotNull(layer.getTextSize().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextSize().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextSize().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextSize().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextSize().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextSize().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 testTextMaxWidthAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textMaxWidth(0.3f));
- assertEquals((Float) layer.getTextMaxWidth().getValue(), (Float) 0.3f);
- }
- });
- }
-
- @Test
- public void testTextMaxWidthAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxWidth(
- zoom(
- exponential(
- stop(2, textMaxWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextMaxWidth());
- assertNotNull(layer.getTextMaxWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getTextMaxWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextMaxWidthAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextMaxWidth());
- assertNotNull(layer.getTextMaxWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textMaxWidth(0.3f));
+ assertEquals((Float) layer.getTextMaxWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextMaxWidthAsExponentialSourceFunction() {
+ public void testTextMaxWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textMaxWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextMaxWidth());
- assertNotNull(layer.getTextMaxWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-max-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextMaxWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textMaxWidth(0.3f))
- )
- ).withDefaultValue(textMaxWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextMaxWidth());
- assertNotNull(layer.getTextMaxWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextMaxWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextMaxWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextMaxWidth().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textMaxWidth(expression));
+ assertEquals(layer.getTextMaxWidth().getExpression(), expression);
});
-
}
- @Test
- public void testTextMaxWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-max-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textMaxWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textMaxWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextMaxWidth());
- assertNotNull(layer.getTextMaxWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextMaxWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextMaxWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextMaxWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextMaxWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextMaxWidth().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 testTextLineHeightAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-line-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textLineHeight(0.3f));
- assertEquals((Float) layer.getTextLineHeight().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextLineHeightAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-line-height");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLineHeight(
- zoom(
- exponential(
- stop(2, textLineHeight(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextLineHeight());
- assertNotNull(layer.getTextLineHeight().getFunction());
- assertEquals(CameraFunction.class, layer.getTextLineHeight().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextLineHeight().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextLineHeight().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextLineHeight().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textLineHeight(0.3f));
+ assertEquals((Float) layer.getTextLineHeight().getValue(), (Float) 0.3f);
});
}
@@ -2033,439 +558,102 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textLetterSpacing(0.3f));
- assertEquals((Float) layer.getTextLetterSpacing().getValue(), (Float) 0.3f);
- }
- });
- }
-
- @Test
- public void testTextLetterSpacingAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLetterSpacing(
- zoom(
- exponential(
- stop(2, textLetterSpacing(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextLetterSpacing());
- assertNotNull(layer.getTextLetterSpacing().getFunction());
- assertEquals(CameraFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextLetterSpacingAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLetterSpacing(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextLetterSpacing());
- assertNotNull(layer.getTextLetterSpacing().getFunction());
- assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textLetterSpacing(0.3f));
+ assertEquals((Float) layer.getTextLetterSpacing().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextLetterSpacingAsExponentialSourceFunction() {
+ public void testTextLetterSpacingAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLetterSpacing(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textLetterSpacing(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextLetterSpacing());
- assertNotNull(layer.getTextLetterSpacing().getFunction());
- assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-letter-spacing-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextLetterSpacingAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLetterSpacing(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textLetterSpacing(0.3f))
- )
- ).withDefaultValue(textLetterSpacing(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextLetterSpacing());
- assertNotNull(layer.getTextLetterSpacing().getFunction());
- assertEquals(SourceFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextLetterSpacing().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textLetterSpacing(expression));
+ assertEquals(layer.getTextLetterSpacing().getExpression(), expression);
});
-
}
- @Test
- public void testTextLetterSpacingAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-letter-spacing");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textLetterSpacing(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textLetterSpacing(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textLetterSpacing(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextLetterSpacing());
- assertNotNull(layer.getTextLetterSpacing().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextLetterSpacing().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextLetterSpacing().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextLetterSpacing().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextLetterSpacing().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextLetterSpacing().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 testTextJustifyAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-justify");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textJustify(TEXT_JUSTIFY_LEFT));
- assertEquals((String) layer.getTextJustify().getValue(), (String) TEXT_JUSTIFY_LEFT);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextJustifyAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-justify");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textJustify(
- zoom(
- interval(
- stop(2, textJustify(TEXT_JUSTIFY_LEFT))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextJustify());
- assertNotNull(layer.getTextJustify().getFunction());
- assertEquals(CameraFunction.class, layer.getTextJustify().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextJustify().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextJustify().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textJustify(TEXT_JUSTIFY_LEFT));
+ assertEquals((String) layer.getTextJustify().getValue(), (String) TEXT_JUSTIFY_LEFT);
});
}
@Test
- public void testTextJustifyAsIdentitySourceFunction() {
+ public void testTextJustifyAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-justify");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textJustify(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextJustify());
- assertNotNull(layer.getTextJustify().getFunction());
- assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextJustify().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-justify-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextJustifyAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-justify");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textJustify(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textJustify(TEXT_JUSTIFY_LEFT))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextJustify());
- assertNotNull(layer.getTextJustify().getFunction());
- assertEquals(SourceFunction.class, layer.getTextJustify().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextJustify().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getTextJustify().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(textJustify(expression));
+ assertEquals(layer.getTextJustify().getExpression(), expression);
});
}
+
@Test
public void testTextAnchorAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textAnchor(TEXT_ANCHOR_CENTER));
- assertEquals((String) layer.getTextAnchor().getValue(), (String) TEXT_ANCHOR_CENTER);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textAnchor(
- zoom(
- interval(
- stop(2, textAnchor(TEXT_ANCHOR_CENTER))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextAnchor());
- assertNotNull(layer.getTextAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getTextAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textAnchor(TEXT_ANCHOR_CENTER));
+ assertEquals((String) layer.getTextAnchor().getValue(), (String) TEXT_ANCHOR_CENTER);
});
}
@Test
- public void testTextAnchorAsIdentitySourceFunction() {
+ public void testTextAnchorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textAnchor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextAnchor());
- assertNotNull(layer.getTextAnchor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-anchor-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextAnchorAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textAnchor(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textAnchor(TEXT_ANCHOR_CENTER))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextAnchor());
- assertNotNull(layer.getTextAnchor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextAnchor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextAnchor().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getTextAnchor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(textAnchor(expression));
+ assertEquals(layer.getTextAnchor().getExpression(), expression);
});
}
+
@Test
public void testTextMaxAngleAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-max-angle");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textMaxAngle(0.3f));
- assertEquals((Float) layer.getTextMaxAngle().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextMaxAngleAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-max-angle");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textMaxAngle(
- zoom(
- exponential(
- stop(2, textMaxAngle(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextMaxAngle());
- assertNotNull(layer.getTextMaxAngle().getFunction());
- assertEquals(CameraFunction.class, layer.getTextMaxAngle().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextMaxAngle().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextMaxAngle().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextMaxAngle().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textMaxAngle(0.3f));
+ assertEquals((Float) layer.getTextMaxAngle().getValue(), (Float) 0.3f);
});
}
@@ -2474,229 +662,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textRotate(0.3f));
- assertEquals((Float) layer.getTextRotate().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextRotateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- zoom(
- exponential(
- stop(2, textRotate(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextRotate());
- assertNotNull(layer.getTextRotate().getFunction());
- assertEquals(CameraFunction.class, layer.getTextRotate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextRotate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextRotate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextRotate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textRotate(0.3f));
+ assertEquals((Float) layer.getTextRotate().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextRotateAsIdentitySourceFunction() {
+ public void testTextRotateAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextRotate());
- assertNotNull(layer.getTextRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getTextRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextRotate().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextRotate().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-rotate-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextRotateAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textRotate(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextRotate());
- assertNotNull(layer.getTextRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getTextRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextRotate().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextRotate().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textRotate(expression));
+ assertEquals(layer.getTextRotate().getExpression(), expression);
});
}
- @Test
- public void testTextRotateAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textRotate(0.3f))
- )
- ).withDefaultValue(textRotate(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextRotate());
- assertNotNull(layer.getTextRotate().getFunction());
- assertEquals(SourceFunction.class, layer.getTextRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextRotate().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextRotate().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextRotate().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextRotate().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextRotate().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testTextRotateAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-rotate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textRotate(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textRotate(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textRotate(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextRotate());
- assertNotNull(layer.getTextRotate().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextRotate().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextRotate().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextRotate().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextRotate().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextRotate().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 testTextPaddingAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textPadding(0.3f));
- assertEquals((Float) layer.getTextPadding().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextPaddingAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-padding");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textPadding(
- zoom(
- exponential(
- stop(2, textPadding(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextPadding());
- assertNotNull(layer.getTextPadding().getFunction());
- assertEquals(CameraFunction.class, layer.getTextPadding().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextPadding().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextPadding().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextPadding().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textPadding(0.3f));
+ assertEquals((Float) layer.getTextPadding().getValue(), (Float) 0.3f);
});
}
@@ -2705,46 +706,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-keep-upright");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textKeepUpright(true));
- assertEquals((Boolean) layer.getTextKeepUpright().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextKeepUprightAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-keep-upright");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textKeepUpright(
- zoom(
- interval(
- stop(2, textKeepUpright(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextKeepUpright());
- assertNotNull(layer.getTextKeepUpright().getFunction());
- assertEquals(CameraFunction.class, layer.getTextKeepUpright().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextKeepUpright().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextKeepUpright().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textKeepUpright(true));
+ assertEquals((Boolean) layer.getTextKeepUpright().getValue(), (Boolean) true);
});
}
@@ -2753,209 +720,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-transform");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textTransform(TEXT_TRANSFORM_NONE));
- assertEquals((String) layer.getTextTransform().getValue(), (String) TEXT_TRANSFORM_NONE);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextTransformAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-transform");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTransform(
- zoom(
- interval(
- stop(2, textTransform(TEXT_TRANSFORM_NONE))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextTransform());
- assertNotNull(layer.getTextTransform().getFunction());
- assertEquals(CameraFunction.class, layer.getTextTransform().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextTransform().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextTransform().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textTransform(TEXT_TRANSFORM_NONE));
+ assertEquals((String) layer.getTextTransform().getValue(), (String) TEXT_TRANSFORM_NONE);
});
}
@Test
- public void testTextTransformAsIdentitySourceFunction() {
+ public void testTextTransformAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-transform");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTransform(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextTransform());
- assertNotNull(layer.getTextTransform().getFunction());
- assertEquals(SourceFunction.class, layer.getTextTransform().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextTransform().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextTransform().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-transform-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextTransformAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-transform");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTransform(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textTransform(TEXT_TRANSFORM_NONE))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextTransform());
- assertNotNull(layer.getTextTransform().getFunction());
- assertEquals(SourceFunction.class, layer.getTextTransform().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextTransform().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getTextTransform().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = string(Expression.get("undefined"));
+ layer.setProperties(textTransform(expression));
+ assertEquals(layer.getTextTransform().getExpression(), expression);
});
}
- @Test
- public void testTextOffsetAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textOffset(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getTextOffset().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
@Test
- public void testTextOffsetAsCameraFunction() {
+ public void testTextOffsetAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOffset(
- zoom(
- exponential(
- stop(2, textOffset(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOffset());
- assertNotNull(layer.getTextOffset().getFunction());
- assertEquals(CameraFunction.class, layer.getTextOffset().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextOffset().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextOffset().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextOffset().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextOffsetAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOffset(property("FeaturePropertyA", Stops.<Float[]>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextOffset());
- assertNotNull(layer.getTextOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getTextOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOffset().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextOffset().getFunction().getStops().getClass());
- }
- });
- }
-
- @Test
- public void testTextOffsetAsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-offset");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOffset(
- property(
- "FeaturePropertyA",
- interval(
- stop(1, textOffset(new Float[] {0f, 0f}))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOffset());
- assertNotNull(layer.getTextOffset().getFunction());
- assertEquals(SourceFunction.class, layer.getTextOffset().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOffset().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.getTextOffset().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textOffset(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getTextOffset().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -2964,46 +764,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-allow-overlap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textAllowOverlap(true));
- assertEquals((Boolean) layer.getTextAllowOverlap().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextAllowOverlapAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-allow-overlap");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textAllowOverlap(
- zoom(
- interval(
- stop(2, textAllowOverlap(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextAllowOverlap());
- assertNotNull(layer.getTextAllowOverlap().getFunction());
- assertEquals(CameraFunction.class, layer.getTextAllowOverlap().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextAllowOverlap().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextAllowOverlap().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textAllowOverlap(true));
+ assertEquals((Boolean) layer.getTextAllowOverlap().getValue(), (Boolean) true);
});
}
@@ -3012,46 +778,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-ignore-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textIgnorePlacement(true));
- assertEquals((Boolean) layer.getTextIgnorePlacement().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextIgnorePlacementAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-ignore-placement");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textIgnorePlacement(
- zoom(
- interval(
- stop(2, textIgnorePlacement(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextIgnorePlacement());
- assertNotNull(layer.getTextIgnorePlacement().getFunction());
- assertEquals(CameraFunction.class, layer.getTextIgnorePlacement().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextIgnorePlacement().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextIgnorePlacement().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textIgnorePlacement(true));
+ assertEquals((Boolean) layer.getTextIgnorePlacement().getValue(), (Boolean) true);
});
}
@@ -3060,46 +792,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-optional");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textOptional(true));
- assertEquals((Boolean) layer.getTextOptional().getValue(), (Boolean) true);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextOptionalAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-optional");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOptional(
- zoom(
- interval(
- stop(2, textOptional(true))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOptional());
- assertNotNull(layer.getTextOptional().getFunction());
- assertEquals(CameraFunction.class, layer.getTextOptional().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextOptional().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextOptional().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textOptional(true));
+ assertEquals((Boolean) layer.getTextOptional().getValue(), (Boolean) true);
});
}
@@ -3108,16 +806,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-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.setIconOpacityTransition(options);
- assertEquals(layer.getIconOpacityTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconOpacityTransition(options);
+ assertEquals(layer.getIconOpacityTransition(), options);
});
}
@@ -3126,198 +821,43 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconOpacity(0.3f));
- assertEquals((Float) layer.getIconOpacity().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconOpacityAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- zoom(
- exponential(
- stop(2, iconOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOpacity());
- assertNotNull(layer.getIconOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getIconOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconOpacity(0.3f));
+ assertEquals((Float) layer.getIconOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testIconOpacityAsIdentitySourceFunction() {
+ public void testIconOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconOpacity());
- assertNotNull(layer.getIconOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconOpacityAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOpacity());
- assertNotNull(layer.getIconOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(iconOpacity(expression));
+ assertEquals(layer.getIconOpacity().getExpression(), expression);
});
}
- @Test
- public void testIconOpacityAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, iconOpacity(0.3f))
- )
- ).withDefaultValue(iconOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOpacity());
- assertNotNull(layer.getIconOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getIconOpacity().getFunction()).getDefaultValue().getValue());
- }
- });
-
- }
-
- @Test
- public void testIconOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, iconOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(iconOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconOpacity());
- assertNotNull(layer.getIconOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getIconOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconOpacity().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 testIconColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("icon-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.setIconColorTransition(options);
- assertEquals(layer.getIconColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconColorTransition(options);
+ assertEquals(layer.getIconColorTransition(), options);
});
}
@@ -3326,157 +866,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getIconColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(
- zoom(
- exponential(
- stop(2, iconColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconColor());
- assertNotNull(layer.getIconColor().getFunction());
- assertEquals(CameraFunction.class, layer.getIconColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testIconColorAsIdentitySourceFunction() {
+ public void testIconColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconColor());
- assertNotNull(layer.getIconColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, iconColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconColor());
- assertNotNull(layer.getIconColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(iconColor(expression));
+ assertEquals(layer.getIconColor().getExpression(), expression);
});
}
- @Test
- public void testIconColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", iconColor(Color.RED))
- )
- ).withDefaultValue(iconColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconColor());
- assertNotNull(layer.getIconColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getIconColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testIconColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconColor(Color.RED));
- assertEquals(layer.getIconColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconColor(Color.RED));
+ assertEquals(layer.getIconColorAsInt(), Color.RED);
});
}
@@ -3485,16 +910,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-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.setIconHaloColorTransition(options);
- assertEquals(layer.getIconHaloColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloColorTransition(options);
+ assertEquals(layer.getIconHaloColorTransition(), options);
});
}
@@ -3503,157 +925,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconHaloColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getIconHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(
- zoom(
- exponential(
- stop(2, iconHaloColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloColor());
- assertNotNull(layer.getIconHaloColor().getFunction());
- assertEquals(CameraFunction.class, layer.getIconHaloColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconHaloColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconHaloColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconHaloColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getIconHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testIconHaloColorAsIdentitySourceFunction() {
+ public void testIconHaloColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconHaloColor());
- assertNotNull(layer.getIconHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconHaloColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-halo-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, iconHaloColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloColor());
- assertNotNull(layer.getIconHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconHaloColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(iconHaloColor(expression));
+ assertEquals(layer.getIconHaloColor().getExpression(), expression);
});
}
- @Test
- public void testIconHaloColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", iconHaloColor(Color.RED))
- )
- ).withDefaultValue(iconHaloColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloColor());
- assertNotNull(layer.getIconHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconHaloColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconHaloColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconHaloColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getIconHaloColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testIconHaloColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconHaloColor(Color.RED));
- assertEquals(layer.getIconHaloColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(iconHaloColor(Color.RED));
+ assertEquals(layer.getIconHaloColorAsInt(), Color.RED);
});
}
@@ -3662,216 +969,58 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-widthTransitionOptions");
- 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.setIconHaloWidthTransition(options);
- assertEquals(layer.getIconHaloWidthTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloWidthAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconHaloWidth(0.3f));
- assertEquals((Float) layer.getIconHaloWidth().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloWidthTransition(options);
+ assertEquals(layer.getIconHaloWidthTransition(), options);
});
}
@Test
- public void testIconHaloWidthAsCameraFunction() {
+ public void testIconHaloWidthAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- zoom(
- exponential(
- stop(2, iconHaloWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloWidth());
- assertNotNull(layer.getIconHaloWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getIconHaloWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconHaloWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconHaloWidth().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloWidthAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconHaloWidth());
- assertNotNull(layer.getIconHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(iconHaloWidth(0.3f));
+ assertEquals((Float) layer.getIconHaloWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testIconHaloWidthAsExponentialSourceFunction() {
+ public void testIconHaloWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconHaloWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloWidth());
- assertNotNull(layer.getIconHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-halo-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, iconHaloWidth(0.3f))
- )
- ).withDefaultValue(iconHaloWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloWidth());
- assertNotNull(layer.getIconHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconHaloWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconHaloWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getIconHaloWidth().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(iconHaloWidth(expression));
+ assertEquals(layer.getIconHaloWidth().getExpression(), expression);
});
-
}
- @Test
- public void testIconHaloWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, iconHaloWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(iconHaloWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloWidth());
- assertNotNull(layer.getIconHaloWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getIconHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getIconHaloWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconHaloWidth().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 testIconHaloBlurTransition() {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blurTransitionOptions");
- 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.setIconHaloBlurTransition(options);
- assertEquals(layer.getIconHaloBlurTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconHaloBlurTransition(options);
+ assertEquals(layer.getIconHaloBlurTransition(), options);
});
}
@@ -3880,198 +1029,43 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconHaloBlur(0.3f));
- assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloBlurAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- zoom(
- exponential(
- stop(2, iconHaloBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloBlur());
- assertNotNull(layer.getIconHaloBlur().getFunction());
- assertEquals(CameraFunction.class, layer.getIconHaloBlur().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconHaloBlur(0.3f));
+ assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f);
});
}
@Test
- public void testIconHaloBlurAsIdentitySourceFunction() {
+ public void testIconHaloBlurAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getIconHaloBlur());
- assertNotNull(layer.getIconHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass());
- }
- });
- }
-
- @Test
- public void testIconHaloBlurAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, iconHaloBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloBlur());
- assertNotNull(layer.getIconHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("icon-halo-blur-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconHaloBlurAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, iconHaloBlur(0.3f))
- )
- ).withDefaultValue(iconHaloBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloBlur());
- assertNotNull(layer.getIconHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getIconHaloBlur().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getIconHaloBlur().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getIconHaloBlur().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(iconHaloBlur(expression));
+ assertEquals(layer.getIconHaloBlur().getExpression(), expression);
});
-
}
- @Test
- public void testIconHaloBlurAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconHaloBlur(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, iconHaloBlur(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(iconHaloBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getIconHaloBlur());
- assertNotNull(layer.getIconHaloBlur().getFunction());
- assertEquals(CompositeFunction.class, layer.getIconHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getIconHaloBlur().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 testIconTranslateTransition() {
validateTestSetup();
setupLayer();
Timber.i("icon-translateTransitionOptions");
- 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.setIconTranslateTransition(options);
- assertEquals(layer.getIconTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setIconTranslateTransition(options);
+ assertEquals(layer.getIconTranslateTransition(), options);
});
}
@@ -4080,47 +1074,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTranslate(
- zoom(
- exponential(
- stop(2, iconTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconTranslate());
- assertNotNull(layer.getIconTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getIconTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getIconTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getIconTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getIconTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -4129,46 +1088,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("icon-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getIconTranslateAnchor().getValue(), (String) ICON_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testIconTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("icon-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- iconTranslateAnchor(
- zoom(
- interval(
- stop(2, iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getIconTranslateAnchor());
- assertNotNull(layer.getIconTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getIconTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getIconTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getIconTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(iconTranslateAnchor(ICON_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getIconTranslateAnchor().getValue(), (String) ICON_TRANSLATE_ANCHOR_MAP);
});
}
@@ -4177,216 +1102,58 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-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.setTextOpacityTransition(options);
- assertEquals(layer.getTextOpacityTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextOpacityAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textOpacity(0.3f));
- assertEquals((Float) layer.getTextOpacity().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextOpacityTransition(options);
+ assertEquals(layer.getTextOpacityTransition(), options);
});
}
@Test
- public void testTextOpacityAsCameraFunction() {
+ public void testTextOpacityAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- zoom(
- exponential(
- stop(2, textOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOpacity());
- assertNotNull(layer.getTextOpacity().getFunction());
- assertEquals(CameraFunction.class, layer.getTextOpacity().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextOpacityAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextOpacity());
- assertNotNull(layer.getTextOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextOpacity().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textOpacity(0.3f));
+ assertEquals((Float) layer.getTextOpacity().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextOpacityAsExponentialSourceFunction() {
+ public void testTextOpacityAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textOpacity(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOpacity());
- assertNotNull(layer.getTextOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-opacity-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextOpacityAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textOpacity(0.3f))
- )
- ).withDefaultValue(textOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOpacity());
- assertNotNull(layer.getTextOpacity().getFunction());
- assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextOpacity().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextOpacity().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextOpacity().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextOpacity().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textOpacity(expression));
+ assertEquals(layer.getTextOpacity().getExpression(), expression);
});
-
}
- @Test
- public void testTextOpacityAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-opacity");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textOpacity(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textOpacity(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textOpacity(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextOpacity());
- assertNotNull(layer.getTextOpacity().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextOpacity().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextOpacity().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextOpacity().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 testTextColorTransition() {
validateTestSetup();
setupLayer();
Timber.i("text-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.setTextColorTransition(options);
- assertEquals(layer.getTextColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextColorTransition(options);
+ assertEquals(layer.getTextColorTransition(), options);
});
}
@@ -4395,157 +1162,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(
- zoom(
- exponential(
- stop(2, textColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextColor());
- assertNotNull(layer.getTextColor().getFunction());
- assertEquals(CameraFunction.class, layer.getTextColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testTextColorAsIdentitySourceFunction() {
+ public void testTextColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextColor());
- assertNotNull(layer.getTextColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, textColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextColor());
- assertNotNull(layer.getTextColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(textColor(expression));
+ assertEquals(layer.getTextColor().getExpression(), expression);
});
}
- @Test
- public void testTextColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", textColor(Color.RED))
- )
- ).withDefaultValue(textColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextColor());
- assertNotNull(layer.getTextColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getTextColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testTextColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textColor(Color.RED));
- assertEquals(layer.getTextColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(textColor(Color.RED));
+ assertEquals(layer.getTextColorAsInt(), Color.RED);
});
}
@@ -4554,16 +1206,13 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-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.setTextHaloColorTransition(options);
- assertEquals(layer.getTextHaloColorTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloColorTransition(options);
+ assertEquals(layer.getTextHaloColorTransition(), options);
});
}
@@ -4572,157 +1221,42 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textHaloColor("rgba(0, 0, 0, 1)"));
- assertEquals((String) layer.getTextHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloColorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(
- zoom(
- exponential(
- stop(2, textHaloColor("rgba(0, 0, 0, 1)"))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloColor());
- assertNotNull(layer.getTextHaloColor().getFunction());
- assertEquals(CameraFunction.class, layer.getTextHaloColor().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextHaloColor().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextHaloColor().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextHaloColor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textHaloColor("rgba(0, 0, 0, 1)"));
+ assertEquals((String) layer.getTextHaloColor().getValue(), (String) "rgba(0, 0, 0, 1)");
});
}
@Test
- public void testTextHaloColorAsIdentitySourceFunction() {
+ public void testTextHaloColorAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(property("FeaturePropertyA", Stops.<String>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextHaloColor());
- assertNotNull(layer.getTextHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextHaloColor().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-halo-color-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloColorAsExponentialSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(
- property(
- "FeaturePropertyA",
- exponential(
- stop(Color.RED, textHaloColor(Color.RED))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloColor());
- assertNotNull(layer.getTextHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextHaloColor().getFunction().getStops().getClass());
- }
+ // Set and Get
+ Expression expression = toColor(Expression.get("undefined"));
+ layer.setProperties(textHaloColor(expression));
+ assertEquals(layer.getTextHaloColor().getExpression(), expression);
});
}
- @Test
- public void testTextHaloColorAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloColor(
- property(
- "FeaturePropertyA",
- categorical(
- stop("valueA", textHaloColor(Color.RED))
- )
- ).withDefaultValue(textHaloColor(Color.GREEN))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloColor());
- assertNotNull(layer.getTextHaloColor().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextHaloColor().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextHaloColor().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextHaloColor().getFunction()).getDefaultValue().getValue());
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.getTextHaloColor().getFunction()).getDefaultValue().getColorInt());
- }
- });
-
- }
@Test
public void testTextHaloColorAsIntConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-halo-color");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textHaloColor(Color.RED));
- assertEquals(layer.getTextHaloColorAsInt(), Color.RED);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ layer.setProperties(textHaloColor(Color.RED));
+ assertEquals(layer.getTextHaloColorAsInt(), Color.RED);
});
}
@@ -4731,416 +1265,103 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-halo-widthTransitionOptions");
- 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.setTextHaloWidthTransition(options);
- assertEquals(layer.getTextHaloWidthTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloWidthAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textHaloWidth(0.3f));
- assertEquals((Float) layer.getTextHaloWidth().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloWidthTransition(options);
+ assertEquals(layer.getTextHaloWidthTransition(), options);
});
}
@Test
- public void testTextHaloWidthAsCameraFunction() {
+ public void testTextHaloWidthAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- zoom(
- exponential(
- stop(2, textHaloWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloWidth());
- assertNotNull(layer.getTextHaloWidth().getFunction());
- assertEquals(CameraFunction.class, layer.getTextHaloWidth().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloWidthAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextHaloWidth());
- assertNotNull(layer.getTextHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textHaloWidth(0.3f));
+ assertEquals((Float) layer.getTextHaloWidth().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextHaloWidthAsExponentialSourceFunction() {
+ public void testTextHaloWidthAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textHaloWidth(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloWidth());
- assertNotNull(layer.getTextHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-halo-width-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloWidthAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textHaloWidth(0.3f))
- )
- ).withDefaultValue(textHaloWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloWidth());
- assertNotNull(layer.getTextHaloWidth().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextHaloWidth().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextHaloWidth().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextHaloWidth().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textHaloWidth(expression));
+ assertEquals(layer.getTextHaloWidth().getExpression(), expression);
});
-
}
- @Test
- public void testTextHaloWidthAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-width");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloWidth(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textHaloWidth(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textHaloWidth(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloWidth());
- assertNotNull(layer.getTextHaloWidth().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextHaloWidth().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloWidth().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextHaloWidth().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 testTextHaloBlurTransition() {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blurTransitionOptions");
- 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.setTextHaloBlurTransition(options);
- assertEquals(layer.getTextHaloBlurTransition(), options);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloBlurAsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textHaloBlur(0.3f));
- assertEquals((Float) layer.getTextHaloBlur().getValue(), (Float) 0.3f);
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextHaloBlurTransition(options);
+ assertEquals(layer.getTextHaloBlurTransition(), options);
});
}
@Test
- public void testTextHaloBlurAsCameraFunction() {
+ public void testTextHaloBlurAsConstant() {
validateTestSetup();
setupLayer();
Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- zoom(
- exponential(
- stop(2, textHaloBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloBlur());
- assertNotNull(layer.getTextHaloBlur().getFunction());
- assertEquals(CameraFunction.class, layer.getTextHaloBlur().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).size());
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloBlurAsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(property("FeaturePropertyA", Stops.<Float>identity()))
- );
-
- // Verify
- assertNotNull(layer.getTextHaloBlur());
- assertNotNull(layer.getTextHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(textHaloBlur(0.3f));
+ assertEquals((Float) layer.getTextHaloBlur().getValue(), (Float) 0.3f);
});
}
@Test
- public void testTextHaloBlurAsExponentialSourceFunction() {
+ public void testTextHaloBlurAsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- property(
- "FeaturePropertyA",
- exponential(
- stop(0.3f, textHaloBlur(0.3f))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloBlur());
- assertNotNull(layer.getTextHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass());
- }
- });
- }
+ Timber.i("text-halo-blur-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextHaloBlurAsCategoricalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- property(
- "FeaturePropertyA",
- categorical(
- stop(1.0f, textHaloBlur(0.3f))
- )
- ).withDefaultValue(textHaloBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloBlur());
- assertNotNull(layer.getTextHaloBlur().getFunction());
- assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.getTextHaloBlur().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.getTextHaloBlur().getFunction()).getDefaultValue().getValue());
- assertEquals(0.3f, ((SourceFunction) layer.getTextHaloBlur().getFunction()).getDefaultValue().getValue());
- }
+ // Set and Get
+ Expression expression = number(Expression.get("undefined"));
+ layer.setProperties(textHaloBlur(expression));
+ assertEquals(layer.getTextHaloBlur().getExpression(), expression);
});
-
}
- @Test
- public void testTextHaloBlurAsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-halo-blur");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textHaloBlur(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, textHaloBlur(0.9f))
- ).withBase(0.5f)
- ).withDefaultValue(textHaloBlur(0.3f))
- )
- );
-
- // Verify
- assertNotNull(layer.getTextHaloBlur());
- assertNotNull(layer.getTextHaloBlur().getFunction());
- assertEquals(CompositeFunction.class, layer.getTextHaloBlur().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloBlur().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getTextHaloBlur().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 testTextTranslateTransition() {
validateTestSetup();
setupLayer();
Timber.i("text-translateTransitionOptions");
- 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.setTextTranslateTransition(options);
- assertEquals(layer.getTextTranslateTransition(), options);
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.setTextTranslateTransition(options);
+ assertEquals(layer.getTextTranslateTransition(), options);
});
}
@@ -5149,47 +1370,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textTranslate(new Float[] {0f, 0f}));
- assertEquals((Float[]) layer.getTextTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextTranslateAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-translate");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTranslate(
- zoom(
- exponential(
- stop(2, textTranslate(new Float[] {0f, 0f}))
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextTranslate());
- assertNotNull(layer.getTextTranslate().getFunction());
- assertEquals(CameraFunction.class, layer.getTextTranslate().getFunction().getClass());
- assertEquals(ExponentialStops.class, layer.getTextTranslate().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.getTextTranslate().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.getTextTranslate().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textTranslate(new Float[] {0f, 0f}));
+ assertEquals((Float[]) layer.getTextTranslate().getValue(), (Float[]) new Float[] {0f, 0f});
});
}
@@ -5198,47 +1384,12 @@ public class SymbolLayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("text-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP));
- assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP);
- }
- });
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- @Test
- public void testTextTranslateAnchorAsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("text-translate-anchor");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- textTranslateAnchor(
- zoom(
- interval(
- stop(2, textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP))
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.getTextTranslateAnchor());
- assertNotNull(layer.getTextTranslateAnchor().getFunction());
- assertEquals(CameraFunction.class, layer.getTextTranslateAnchor().getFunction().getClass());
- assertEquals(IntervalStops.class, layer.getTextTranslateAnchor().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.getTextTranslateAnchor().getFunction().getStops()).size());
- }
+ // Set and Get
+ layer.setProperties(textTranslateAnchor(TEXT_TRANSLATE_ANCHOR_MAP));
+ assertEquals((String) layer.getTextTranslateAnchor().getValue(), (String) TEXT_TRANSLATE_ANCHOR_MAP);
});
}
-
-}
+} \ No newline at end of file
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..935813899f 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs
@@ -7,31 +7,18 @@
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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.<%- camelize(type) %>Layer;
-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.style.expressions.Expression.*;
import static com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke;
import static org.junit.Assert.*;
import static com.mapbox.mapboxsdk.style.layers.Property.*;
@@ -56,25 +43,19 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
private void setupLayer() {
<% if (type === 'background') { -%>
Timber.i("Retrieving layer");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- layer = mapboxMap.getLayerAs("background");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ layer = mapboxMap.getLayerAs("background");
});
<% } else { -%>
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 <%- camelize(type) %>Layer("my-layer", "composite");
- layer.setSourceLayer("composite");
- mapboxMap.addLayer(layer);
- // Layer reference is now stale, get new reference
- layer = mapboxMap.getLayerAs("my-layer");
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ if ((layer = mapboxMap.getLayerAs("my-layer")) == null) {
+ Timber.i("Adding layer");
+ layer = new <%- camelize(type) %>Layer("my-layer", "composite");
+ layer.setSourceLayer("composite");
+ mapboxMap.addLayer(layer);
+ // Layer reference is now stale, get new reference
+ layer = mapboxMap.getLayerAs("my-layer");
}
});
<% } -%>
@@ -85,355 +66,108 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("Visibility");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getVisibility().getValue(), VISIBLE);
+ // Get initial
+ assertEquals(layer.getVisibility().getValue(), VISIBLE);
- // Set
- layer.setProperties(visibility(NONE));
- assertEquals(layer.getVisibility().getValue(), NONE);
- }
+ // Set
+ layer.setProperties(visibility(NONE));
+ assertEquals(layer.getVisibility().getValue(), NONE);
});
}
-<% if (!(type === 'background' || type === 'raster')) { -%>
+<% if (!(type === 'background' || type === 'raster' || type === 'hillshade')) { -%>
@Test
public void testSourceLayer() {
validateTestSetup();
setupLayer();
Timber.i("SourceLayer");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Get initial
- assertEquals(layer.getSourceLayer(), "composite");
+ // Get initial
+ assertEquals(layer.getSourceLayer(), "composite");
- // Set
- final String sourceLayer = "test";
- layer.setSourceLayer(sourceLayer);
- assertEquals(layer.getSourceLayer(), sourceLayer);
- }
+ // Set
+ final String sourceLayer = "test";
+ layer.setSourceLayer(sourceLayer);
+ assertEquals(layer.getSourceLayer(), sourceLayer);
});
}
-<% } -%>
-<% for (const property of properties) { -%>
-<% if (property.transition) { -%>
@Test
- public void test<%- camelize(property.name) %>Transition() {
+ public void testFilter() {
validateTestSetup();
setupLayer();
- Timber.i("<%- property.name %>TransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ Timber.i("Filter");
+ invoke(mapboxMap, (uiController, mapboxMap1) -> {
+ assertNotNull(layer);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- layer.set<%- camelize(property.name) %>Transition(options);
- assertEquals(layer.get<%- camelize(property.name) %>Transition(), options);
- }
- });
- }
-<% } -%>
+ // Get initial
+ assertEquals(layer.getFilter(), null);
- @Test
- public void test<%- camelize(property.name) %>AsConstant() {
- validateTestSetup();
- setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set and Get
- layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>));
- assertEquals((<%- propertyType(property) %>) layer.get<%- camelize(property.name) %>().getValue(), (<%- propertyType(property) %>) <%- defaultValueJava(property) %>);
- }
+ // Set
+ Expression filter = eq(get("undefined"), literal(1.0));
+ layer.setFilter(filter);
+ assertEquals(layer.getFilter().toString(), filter.toString());
});
}
-<% if (supportsZoomFunction(property)) { -%>
-
- @Test
- public void test<%- camelize(property.name) %>AsCameraFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- zoom(
-<% if (property.function == 'piecewise-constant') { -%>
- interval(
- stop(2, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- )
-<% } else { -%>
- exponential(
- stop(2, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- ).withBase(0.5f)
-<% } -%>
- )
- )
- );
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(CameraFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
-<% if (property.function == 'piecewise-constant') { -%>
- assertEquals(IntervalStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- assertEquals(1, ((IntervalStops) layer.get<%- camelize(property.name) %>().getFunction().getStops()).size());
-<% } else { -%>
- assertEquals(ExponentialStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- assertEquals(0.5f, ((ExponentialStops) layer.get<%- camelize(property.name) %>().getFunction().getStops()).getBase(), 0.001);
- assertEquals(1, ((ExponentialStops) layer.get<%- camelize(property.name) %>().getFunction().getStops()).size());
-<% } -%>
- }
- });
- }
<% } -%>
-<% if (supportsPropertyFunction(property)) { -%>
-
- @Test
- public void test<%- camelize(property.name) %>AsIdentitySourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(property("FeaturePropertyA", Stops.<<%- propertyType(property) %>>identity()))
- );
-
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(SourceFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(IdentityStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- }
- });
- }
-<% if (property.function == 'piecewise-constant') { -%>
+<% for (const property of properties) { -%>
+<% if (property.name != 'heatmap-color') { -%>
+<% if (property.transition) { -%>
@Test
- public void test<%- camelize(property.name) %>AsIntervalSourceFunction() {
+ public void test<%- camelize(property.name) %>Transition() {
validateTestSetup();
setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- interval(
-<% if (property.type == 'color') { -%>
- stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
-<% } else {-%>
- stop(1, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- )
-<% } -%>
- )
- )
- );
+ Timber.i("<%- property.name %>TransitionOptions");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(SourceFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- }
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ layer.set<%- camelize(property.name) %>Transition(options);
+ assertEquals(layer.get<%- camelize(property.name) %>Transition(), options);
});
}
-<% } else if (property.type === 'array') { -%>
-
- @Test
- public void test<%- camelize(property.name) %>AsIntervalSourceFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- interval(
-<% if (property.type == 'color') { -%>
- stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
-<% } else {-%>
- stop(1, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
<% } -%>
- )
- )
- )
- );
-
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(SourceFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(IntervalStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- }
- });
- }
-<% } else { -%>
@Test
- public void test<%- camelize(property.name) %>AsExponentialSourceFunction() {
+ public void test<%- camelize(property.name) %>AsConstant() {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- exponential(
-<% if (property.type == 'color') { -%>
- stop(Color.RED, <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
-<% } else {-%>
- stop(<%- defaultValueJava(property) %>, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
-<% } -%>
- ).withBase(0.5f)
- )
- )
- );
-
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(SourceFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- }
+ // Set and Get
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>));
+ assertEquals((<%- propertyType(property) %>) layer.get<%- camelize(property.name) %>().getValue(), (<%- propertyType(property) %>) <%- defaultValueJava(property) %>);
});
}
+<% if (isDataDriven(property)) { -%>
+<% if (!(property.name.endsWith("-font")||property.name.endsWith("-offset"))) { -%>
@Test
- public void test<%- camelize(property.name) %>AsCategoricalSourceFunction() {
+ public void test<%- camelize(property.name) %>AsExpression() {
validateTestSetup();
setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
-
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- property(
- "FeaturePropertyA",
- categorical(
-<% if (property.type == 'color') { -%>
- stop("valueA", <%- camelizeWithLeadingLowercase(property.name) %>(Color.RED))
- )
- ).withDefaultValue(<%- camelizeWithLeadingLowercase(property.name) %>(Color.GREEN))
-<% } else {-%>
- stop(1.0f, <%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
- )
- ).withDefaultValue(<%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
-<% } -%>
- )
- );
-
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(SourceFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(CategoricalStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- assertNotNull(((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getDefaultValue());
- assertNotNull(((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getDefaultValue().getValue());
-<% if (property.type === 'color') { -%>
- assertEquals(Color.GREEN, (int) ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getDefaultValue().getColorInt());
-<% } else { -%>
- assertEquals(<%- defaultValueJava(property) %>, ((SourceFunction) layer.get<%- camelize(property.name) %>().getFunction()).getDefaultValue().getValue());
-<% } -%>
- }
+ Timber.i("<%- property.name %>-expression");
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
+
+ // Set and Get
+ Expression expression = <%- defaultExpressionJava(property) %>(Expression.get("undefined"));
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(expression));
+ assertEquals(layer.get<%- camelize(property.name) %>().getExpression(), expression);
});
-
}
-<% if (property.type !== 'color') { -%>
-
- @Test
- public void test<%- camelize(property.name) %>AsCompositeFunction() {
- validateTestSetup();
- setupLayer();
- Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
- // Set
- layer.setProperties(
- <%- camelizeWithLeadingLowercase(property.name) %>(
- composite(
- "FeaturePropertyA",
- exponential(
- stop(0, 0.3f, <%- camelizeWithLeadingLowercase(property.name) %>(0.9f))
- ).withBase(0.5f)
-<% if (property.type == 'number') { -%>
- ).withDefaultValue(<%- camelizeWithLeadingLowercase(property.name) %>(<%- defaultValueJava(property) %>))
-<% } else { -%>
- )
-<% } -%>
- )
- );
-
- // Verify
- assertNotNull(layer.get<%- camelize(property.name) %>());
- assertNotNull(layer.get<%- camelize(property.name) %>().getFunction());
- assertEquals(CompositeFunction.class, layer.get<%- camelize(property.name) %>().getFunction().getClass());
- assertEquals("FeaturePropertyA", ((CompositeFunction) layer.get<%- camelize(property.name) %>().getFunction()).getProperty());
- assertEquals(ExponentialStops.class, layer.get<%- camelize(property.name) %>().getFunction().getStops().getClass());
- assertEquals(1, ((ExponentialStops) layer.get<%- camelize(property.name) %>().getFunction().getStops()).size());
-
- ExponentialStops<Stop.CompositeValue<Float, <%- propertyType(property) %>>, <%- propertyType(property) %>> stops =
- (ExponentialStops<Stop.CompositeValue<Float, <%- propertyType(property) %>>, <%- propertyType(property) %>>) layer.get<%- camelize(property.name) %>().getFunction().getStops();
- Stop<Stop.CompositeValue<Float, <%- propertyType(property) %>>, <%- propertyType(property) %>> 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);
- }
- });
- }
-<% } -%>
<% } -%>
<% } -%>
<% if (property.type == 'color') { -%>
@@ -443,18 +177,15 @@ public class <%- camelize(type) %>LayerTest extends BaseActivityTest {
validateTestSetup();
setupLayer();
Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(layer);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(layer);
- // Set and Get
- layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.RED));
- assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.RED);
- }
+ // Set and Get
+ layer.setProperties(<%- camelizeWithLeadingLowercase(property.name) %>(Color.RED));
+ assertEquals(layer.get<%- camelize(property.name) %>AsInt(), Color.RED);
});
}
<% } -%>
<% } -%>
-
-}
+<% } -%>
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
index 2f22a8f3f0..c35168bb7a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs
@@ -9,15 +9,12 @@ 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.light.Light;
-import com.mapbox.mapboxsdk.style.functions.Function;
-import com.mapbox.mapboxsdk.style.functions.stops.IdentityStops;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.TransitionOptions;
import com.mapbox.mapboxsdk.style.light.Position;
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.FillExtrusionStyleTestActivity;
@@ -30,7 +27,7 @@ import org.junit.runner.RunWith;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
import static com.mapbox.mapboxsdk.style.layers.Property.ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
@@ -53,15 +50,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>TransitionOptions");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- TransitionOptions options = new TransitionOptions(300, 100);
- light.set<%- camelize(property.name) %>Transition(options);
- assertEquals("Transition options should match", options, light.get<%- camelize(property.name) %>Transition());
- }
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ TransitionOptions options = new TransitionOptions(300, 100);
+ light.set<%- camelize(property.name) %>Transition(options);
+ assertEquals("Transition options should match", options, light.get<%- camelize(property.name) %>Transition());
});
}
<% } -%>
@@ -72,15 +66,12 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- Position position = new Position(1, 2, 3);
- light.set<%- camelize(property.name) %>(position);
- assertEquals("Position should match", position, light.get<%- camelize(property.name) %>());
- }
+ invoke(mapboxMap,(uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ Position position = new Position(1, 2, 3);
+ light.set<%- camelize(property.name) %>(position);
+ assertEquals("Position should match", position, light.get<%- camelize(property.name) %>());
});
}
<% } else { -%>
@@ -90,18 +81,15 @@ public class LightTest extends BaseActivityTest {
validateTestSetup();
setupLight();
Timber.i("<%- property.name %>");
- invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
- @Override
- public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
- assertNotNull(light);
- // Set and Get
- light.set<%- camelize(property.name) %>(<%- defaultValueJava(property) %>);
+ invoke(mapboxMap, (uiController, mapboxMap) -> {
+ assertNotNull(light);
+ // Set and Get
+ light.set<%- camelize(property.name) %>(<%- defaultValueJava(property) %>);
<% if (property.name == 'color') { -%>
- assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>.replaceAll("\\s+", ""), light.get<%- camelize(property.name) %>());
+ assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>.replaceAll("\\s+", ""), light.get<%- camelize(property.name) %>());
<% } else { -%>
- assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>, light.get<%- camelize(property.name) %>());
+ assertEquals("<%- camelize(property.name) %> should match", <%- defaultValueJava(property) %>, light.get<%- camelize(property.name) %>());
<% } -%>
- }
});
}
<% } -%>
@@ -124,12 +112,12 @@ public class LightTest extends BaseActivityTest {
light = mapboxMap.getLight();
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
- fillExtrusionLayer.setFilter(eq("extrude", "true"));
+ fillExtrusionLayer.setFilter(eq(Expression.get("extrude"), "true"));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
- fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())),
- fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())),
+ fillExtrusionHeight(Expression.get("height")),
+ fillExtrusionBase(Expression.get("min_height")),
fillExtrusionOpacity(0.6f)
);
mapboxMap.addLayer(fillExtrusionLayer);
@@ -141,4 +129,4 @@ public class LightTest extends BaseActivityTest {
protected Class getActivityClass() {
return FillExtrusionStyleTestActivity.class;
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index 6f311788ba..fb1d0ef8a2 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -1,9 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.mapbox.mapboxsdk.testapp">
+ package="com.mapbox.mapboxsdk.testapp">
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".MapboxApplication"
@@ -19,8 +18,9 @@
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
@@ -29,10 +29,10 @@
android:label="@string/activity_info_window">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow"/>
+ android:value="@string/category_infowindow" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.infowindow.InfoWindowAdapterActivity"
@@ -40,10 +40,10 @@
android:label="@string/activity_infowindow_adapter">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow"/>
+ android:value="@string/category_infowindow" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.infowindow.DynamicInfoWindowAdapterActivity"
@@ -51,10 +51,10 @@
android:label="@string/activity_dynamic_infowindow_adapter">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow"/>
+ android:value="@string/category_infowindow" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.BulkMarkerActivity"
@@ -63,21 +63,21 @@
android:label="@string/activity_add_bulk_markers">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
- android:name=".activity.annotation.AnimatedMarkerActivity"
- android:description="@string/description_animated_marker"
- android:label="@string/activity_animated_marker">
+ android:name=".activity.annotation.AnimatedSymbolLayerActivity"
+ android:description="@string/description_animated_symbollayer"
+ android:label="@string/activity_animated_symbollayer">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.DynamicMarkerChangeActivity"
@@ -85,10 +85,10 @@
android:label="@string/activity_dynamic_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.PressForMarkerActivity"
@@ -96,10 +96,10 @@
android:label="@string/activity_press_for_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.CameraAnimationTypeActivity"
@@ -107,10 +107,10 @@
android:label="@string/activity_camera_animation_types">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.CameraAnimatorActivity"
@@ -118,10 +118,10 @@
android:label="@string/activity_camera_animator">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.CameraPositionActivity"
@@ -129,10 +129,10 @@
android:label="@string/activity_camera_position">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.LatLngBoundsActivity"
@@ -141,10 +141,10 @@
android:screenOrientation="portrait">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.fragment.MapFragmentActivity"
@@ -152,10 +152,10 @@
android:label="@string/activity_map_fragment">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment"/>
+ android:value="@string/category_fragment" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.fragment.SupportMapFragmentActivity"
@@ -163,10 +163,10 @@
android:label="@string/activity_map_fragment_suport">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment"/>
+ android:value="@string/category_fragment" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.fragment.MultiMapActivity"
@@ -174,10 +174,10 @@
android:label="@string/activity_multimap">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment"/>
+ android:value="@string/category_fragment" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.ManualZoomActivity"
@@ -185,10 +185,10 @@
android:label="@string/activity_camera_zoom">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.MaxMinZoomActivity"
@@ -196,10 +196,10 @@
android:label="@string/activity_minmax_zoom">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.customlayer.CustomLayerActivity"
@@ -207,66 +207,10 @@
android:label="@string/activity_custom_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_custom_layer"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
- android:name=".activity.userlocation.MyLocationTrackingModeActivity"
- android:description="@string/description_user_location_tracking"
- android:label="@string/activity_user_tracking_mode"
- android:theme="@style/NoActionBar">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_userlocation"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
- android:name=".activity.userlocation.MyLocationDrawableActivity"
- android:description="@string/description_user_location_customization"
- android:label="@string/activity_user_tracking_customization">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_userlocation"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
- android:name=".activity.userlocation.MyLocationTintActivity"
- android:description="@string/description_user_location_dot_color"
- android:label="@string/activity_user_dot_color">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_userlocation"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
- android:name=".activity.userlocation.MyLocationToggleActivity"
- android:description="@string/description_user_location_toggle"
- android:label="@string/activity_user_location_toggle">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_userlocation"/>
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
- </activity>
- <activity
- android:name=".activity.userlocation.CustomLocationEngineActivity"
- android:description="@string/description_custom_location_engine"
- android:label="@string/activity_custom_location_engine">
- <meta-data
- android:name="@string/category"
- android:value="@string/category_userlocation"/>
+ android:value="@string/category_custom_layer" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.PolygonActivity"
@@ -274,10 +218,10 @@
android:label="@string/activity_polygon">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.PolylineActivity"
@@ -285,10 +229,10 @@
android:label="@string/activity_polyline">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.ScrollByActivity"
@@ -297,10 +241,10 @@
android:theme="@style/NoActionBar">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera"/>
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.MapPaddingActivity"
@@ -309,10 +253,10 @@
android:screenOrientation="portrait">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.DebugModeActivity"
@@ -321,7 +265,7 @@
android:label="@string/activity_debug_mode">
<meta-data
android:name="@string/category"
- android:value="@string/category_basic"/>
+ android:value="@string/category_basic" />
</activity>
<activity
android:name=".activity.offline.OfflineActivity"
@@ -329,10 +273,10 @@
android:label="@string/activity_offline">
<meta-data
android:name="@string/category"
- android:value="@string/category_offline"/>
+ android:value="@string/category_offline" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.offline.UpdateMetadataActivity"
@@ -340,10 +284,10 @@
android:label="@string/activity_update_metadata">
<meta-data
android:name="@string/category"
- android:value="@string/category_offline"/>
+ android:value="@string/category_offline" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.offline.DeleteRegionActivity"
@@ -351,10 +295,10 @@
android:label="@string/activity_offline_region_delete">
<meta-data
android:name="@string/category"
- android:value="@string/category_offline"/>
+ android:value="@string/category_offline" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.imagegenerator.SnapshotActivity"
@@ -362,10 +306,10 @@
android:label="@string/activity_snapshot">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator"/>
+ android:value="@string/category_imagegenerator" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.snapshot.MapSnapshotterActivity"
@@ -373,10 +317,10 @@
android:label="@string/activity_map_snapshotter">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator"/>
+ android:value="@string/category_imagegenerator" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.snapshot.MapSnapshotterReuseActivity"
@@ -384,10 +328,10 @@
android:label="@string/activity_map_snapshotter_reuse">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator"/>
+ android:value="@string/category_imagegenerator" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.snapshot.MapSnapshotterMarkerActivity"
@@ -395,10 +339,10 @@
android:label="@string/activity_map_snapshotter_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator"/>
+ android:value="@string/category_imagegenerator" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.DoubleMapActivity"
@@ -406,10 +350,10 @@
android:label="@string/activity_double_map">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.MarkerViewActivity"
@@ -417,10 +361,10 @@
android:label="@string/activity_view_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.fragment.ViewPagerActivity"
@@ -428,10 +372,10 @@
android:label="@string/activity_viewpager">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment"/>
+ android:value="@string/category_fragment" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.SimpleMapActivity"
@@ -439,10 +383,10 @@
android:label="@string/activity_simple_map">
<meta-data
android:name="@string/category"
- android:value="@string/category_basic"/>
+ android:value="@string/category_basic" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.MapChangeActivity"
@@ -450,10 +394,10 @@
android:label="@string/activity_map_change">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.VisibilityChangeActivity"
@@ -461,10 +405,10 @@
android:label="@string/activity_map_visibility">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.RuntimeStyleActivity"
@@ -472,10 +416,10 @@
android:label="@string/activity_runtime_style">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.DataDrivenStyleActivity"
@@ -483,10 +427,10 @@
android:label="@string/activity_data_driven_style">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.CircleLayerActivity"
@@ -494,10 +438,10 @@
android:label="@string/activity_circle_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.FillExtrusionActivity"
@@ -505,10 +449,10 @@
android:label="@string/activity_fill_extrusion_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.BuildingFillExtrusionActivity"
@@ -516,10 +460,10 @@
android:label="@string/activity_building_fill_extrusion_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.SymbolLayerActivity"
@@ -527,10 +471,10 @@
android:label="@string/activity_symbol_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.GeoJsonClusteringActivity"
@@ -538,10 +482,10 @@
android:label="@string/activity_geojson_clustering">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.RealTimeGeoJsonActivity"
@@ -549,10 +493,10 @@
android:label="@string/activity_geojson_realtime">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.StyleFileActivity"
@@ -560,10 +504,10 @@
android:label="@string/activity_style_file">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.CustomSpriteActivity"
@@ -571,10 +515,10 @@
android:label="@string/activity_add_sprite">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.imagegenerator.PrintActivity"
@@ -582,10 +526,10 @@
android:label="@string/activity_print">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator"/>
+ android:value="@string/category_imagegenerator" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.AnimatedImageSourceActivity"
@@ -593,12 +537,22 @@
android:label="@string/activity_animated_image_source">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ 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"
@@ -606,10 +560,10 @@
android:label="@string/activity_query_rendered_feature_properties">
<meta-data
android:name="@string/category"
- android:value="@string/category_features"/>
+ android:value="@string/category_features" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.feature.QueryRenderedFeaturesBoxCountActivity"
@@ -617,10 +571,10 @@
android:label="@string/activity_query_rendered_features_box_count">
<meta-data
android:name="@string/category"
- android:value="@string/category_features"/>
+ android:value="@string/category_features" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.feature.QueryRenderedFeaturesBoxSymbolCountActivity"
@@ -628,10 +582,10 @@
android:label="@string/activity_query_rendered_features_box_symbol_count">
<meta-data
android:name="@string/category"
- android:value="@string/category_features"/>
+ android:value="@string/category_features" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.feature.QueryRenderedFeaturesBoxHighlightActivity"
@@ -639,10 +593,10 @@
android:label="@string/activity_query_rendered_features_box_highlight">
<meta-data
android:name="@string/category"
- android:value="@string/category_features"/>
+ android:value="@string/category_features" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.feature.QuerySourceFeaturesActivity"
@@ -650,10 +604,10 @@
android:label="@string/activity_query_source_features">
<meta-data
android:name="@string/category"
- android:value="@string/category_features"/>
+ android:value="@string/category_features" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.SymbolGeneratorActivity"
@@ -661,10 +615,10 @@
android:label="@string/activity_symbol_generator">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.style.ZoomFunctionSymbolLayerActivity"
@@ -672,10 +626,10 @@
android:label="@string/activity_add_remove_markers">
<meta-data
android:name="@string/category"
- android:value="@string/category_style"/>
+ android:value="@string/category_style" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.MapInDialogActivity"
@@ -683,10 +637,10 @@
android:label="@string/activity_map_in_dialog">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.MarkerViewsInRectangleActivity"
@@ -694,10 +648,10 @@
android:label="@string/activity_marker_view_rectangle">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation"/>
+ android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.LatLngBoundsForCameraActivity"
@@ -705,10 +659,10 @@
android:label="@string/activity_restricted_bounds">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<!-- Storage -->
@@ -718,10 +672,10 @@
android:label="@string/activity_url_transform">
<meta-data
android:name="@string/category"
- android:value="@string/category_storage"/>
+ android:value="@string/category_storage" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.maplayout.BottomSheetActivity"
@@ -729,7 +683,7 @@
android:label="@string/activity_bottom_sheet">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
</activity>
<!-- TextureView -->
@@ -739,7 +693,7 @@
android:label="@string/activity_textureview_debug">
<meta-data
android:name="@string/category"
- android:value="@string/category_textureview"/>
+ android:value="@string/category_textureview" />
</activity>
<activity
android:name=".activity.textureview.TextureViewResizeActivity"
@@ -747,7 +701,18 @@
android:label="@string/activity_textureview_resize">
<meta-data
android:name="@string/category"
- android:value="@string/category_textureview"/>
+ android:value="@string/category_textureview" />
+ </activity>
+ <activity
+ android:name=".activity.textureview.TextureViewTransparentBackgroundActivity"
+ android:description="@string/description_textureview_transparent"
+ android:label="@string/activity_textureview_transparent">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_textureview" />
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.textureview.TextureViewAnimationActivity"
@@ -755,7 +720,7 @@
android:label="@string/activity_textureview_animate">
<meta-data
android:name="@string/category"
- android:value="@string/category_textureview"/>
+ android:value="@string/category_textureview" />
</activity>
<activity
android:name=".activity.maplayout.LocalGlyphActivity"
@@ -763,32 +728,65 @@
android:label="@string/activity_local_glyph">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout"/>
+ android:value="@string/category_maplayout" />
+ <meta-data
+ 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>
+ <activity
+ android:name=".activity.camera.GestureDetectorActivity"
+ android:label="@string/activity_gesture_detector"
+ android:description="@string/description_gesture_detector">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_camera" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
- android:value=".activity.FeatureOverviewActivity"/>
+ android:value="com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity" />
</activity>
<!-- For Instrumentation tests -->
<activity
android:name=".activity.style.RuntimeStyleTestActivity"
- android:screenOrientation="portrait"/>
+ android:screenOrientation="portrait" />
<activity
android:name=".activity.style.RuntimeStyleTimingTestActivity"
- android:screenOrientation="portrait"/>
+ android:screenOrientation="portrait" />
<activity
android:name=".activity.espresso.EspressoTestActivity"
- android:screenOrientation="portrait"/>
+ android:screenOrientation="portrait" />
<activity
android:name=".activity.style.FillExtrusionStyleTestActivity"
- android:screenOrientation="portrait"/>
+ android:screenOrientation="portrait" />
<!-- 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"/>
+ android:value="pk.eyJ1IjoiYmxzdGFnaW5nIiwiYSI6ImNpdDF3OHpoaTAwMDcyeXA5Y3Z0Nmk2dzEifQ.0IfB7v5Qbm2MGVYt8Kb8fg" />
<!-- Comment out this setting to switch to external storage (and disable internal) in your app -->
<!-- <meta-data -->
@@ -796,4 +794,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/MapboxApplication.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
index fba33bb380..fa13959112 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java
@@ -5,6 +5,7 @@ import android.os.StrictMode;
import android.text.TextUtils;
import com.mapbox.mapboxsdk.Mapbox;
+import com.mapbox.mapboxsdk.maps.Telemetry;
import com.mapbox.mapboxsdk.testapp.utils.TokenUtils;
import com.squareup.leakcanary.LeakCanary;
@@ -57,6 +58,8 @@ public class MapboxApplication extends Application {
}
Mapbox.getInstance(getApplicationContext(), mapboxAccessToken);
+
+ Telemetry.updateDebugLoggingEnabled(true);
}
private void initializeLogger() {
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java
index 3f20f19f5d..c8b15593ec 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
@@ -7,24 +7,17 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
-import android.support.design.widget.Snackbar;
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 java.util.ArrayList;
import java.util.Collections;
@@ -40,41 +33,29 @@ import timber.log.Timber;
* It uses tags as category and description to order the different entries.
* </p>
*/
-public class FeatureOverviewActivity extends AppCompatActivity implements PermissionsListener {
+public class FeatureOverviewActivity extends AppCompatActivity {
private static final String KEY_STATE_FEATURES = "featureList";
- private PermissionsManager permissionsManager;
private RecyclerView recyclerView;
private FeatureSectionAdapter sectionAdapter;
private List<Feature> features;
- private int locationActivityInList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feature_overview);
- permissionsManager = new PermissionsManager(this);
-
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
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;
- }
- }
- startFeature(feature);
- }
+ ItemClickSupport.addTo(recyclerView).setOnItemClickListener((recyclerView, position, view) -> {
+ if (!sectionAdapter.isSectionHeaderPosition(position)) {
+ int itemPosition = sectionAdapter.getConvertedPosition(position);
+ Feature feature = features.get(itemPosition);
+ startFeature(feature);
}
});
@@ -122,45 +103,6 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
startActivity(intent);
}
- private boolean requestLocationPermission(final int positionInList) {
- if (isRuntimePermissionsRequired()) {
- locationActivityInList = positionInList;
- permissionsManager.requestLocationPermissions(this);
- return true;
- }
- return false;
- }
-
- @Override
- public void onExplanationNeeded(List<String> list) {
- Snackbar.make(
- findViewById(android.R.id.content),
- TextUtils.join("", list.toArray()),
- Snackbar.LENGTH_SHORT).show();
- }
-
- @Override
- public void onPermissionResult(boolean isPermissionGranted) {
- if (isPermissionGranted) {
- startFeature(features.get(locationActivityInList));
- } else {
- Snackbar.make(
- findViewById(android.R.id.content),
- "Can't open without accepting the location permission.",
- Snackbar.LENGTH_SHORT).show();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
- private boolean isRuntimePermissionsRequired() {
- return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- }
-
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -182,21 +124,17 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
String label = getString(info.labelRes);
String description = resolveString(info.descriptionRes);
String category = resolveMetaData(info.metaData, metaDataKey);
- boolean requiresLocationPermission = requiresLocationPermission(label, category);
- features.add(new Feature(info.name, label, description, category, requiresLocationPermission));
+ features.add(new Feature(info.name, label, description, category));
}
}
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);
}
@@ -220,24 +158,6 @@ public class FeatureOverviewActivity extends AppCompatActivity implements Permis
}
}
- private boolean requiresLocationPermission(String name, String category) {
- final Resources resources = getResources();
-
- List<String> requiresPermissionCategories = new ArrayList<String>() {
- {
- add(resources.getString(R.string.category_userlocation));
- }
- };
-
- List<String> requiresPermissionActivities = new ArrayList<String>() {
- {
- add(resources.getString(R.string.activity_double_map));
- }
- };
-
- return requiresPermissionCategories.contains(category) || requiresPermissionActivities.contains(name);
- }
-
@Override
protected void onPostExecute(List<Feature> features) {
super.onPostExecute(features);
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
deleted file mode 100644
index d8752bbea2..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java
+++ /dev/null
@@ -1,300 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.annotation;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-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.mapboxsdk.annotations.Icon;
-import com.mapbox.mapboxsdk.annotations.IconFactory;
-import com.mapbox.mapboxsdk.annotations.Marker;
-import com.mapbox.mapboxsdk.annotations.MarkerView;
-import com.mapbox.mapboxsdk.annotations.MarkerViewManager;
-import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
-import com.mapbox.mapboxsdk.camera.CameraPosition;
-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 java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * Test activity showcasing animating MarkerViews.
- */
-public class AnimatedMarkerActivity extends AppCompatActivity {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
-
- private LatLng dupontCircle = new LatLng(38.90962, -77.04341);
-
- private Marker passengerMarker = null;
- private MarkerView carMarker = null;
-
- private Runnable animationRunnable;
-
- private List<MarkerView> markerViews = new ArrayList<>();
- private boolean stopped;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_animated_marker);
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
-
- @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);
- }
- });
- }
-
- private void setupMap() {
- CameraPosition cameraPosition = new CameraPosition.Builder()
- .target(dupontCircle)
- .zoom(15)
- .build();
- mapboxMap.setCameraPosition(cameraPosition);
- }
-
- private void addPassenger() {
- if (isActivityStopped()) {
- return;
- }
-
- LatLng randomLatLng = getLatLngInBounds();
-
- if (passengerMarker == null) {
- Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_directions_run_black,
- ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme()));
- passengerMarker = mapboxMap.addMarker(new MarkerViewOptions()
- .position(randomLatLng)
- .icon(icon));
- } else {
- passengerMarker.setPosition(randomLatLng);
- }
- }
-
- private void addMainCar() {
- if (isActivityStopped()) {
- return;
- }
-
- LatLng randomLatLng = getLatLngInBounds();
-
- 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);
- }
- });
- markerViews.add(carMarker);
- } else {
- carMarker.setPosition(randomLatLng);
- }
- }
-
- private void animateMoveToPassenger(final MarkerView car) {
- if (isActivityStopped()) {
- return;
- }
-
- ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition());
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- addPassenger();
- animateMoveToPassenger(car);
- }
- });
- }
-
- protected void addRandomCar() {
- markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top,
- new MarkerViewManager.OnMarkerViewAddedListener() {
- @Override
- public void onViewAdded(@NonNull MarkerView markerView) {
- randomlyMoveMarker(markerView);
- }
- }));
- }
-
- private void randomlyMoveMarker(final MarkerView marker) {
- if (isActivityStopped()) {
- return;
- }
-
- ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds());
-
- // Add listener to restart animation on end
- animator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- randomlyMoveMarker(marker);
- }
- });
- }
-
- private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) {
- marker.setRotation((float) getBearing(marker.getPosition(), to));
-
- final ValueAnimator markerAnimator = ObjectAnimator.ofObject(
- marker, "position", new LatLngEvaluator(), marker.getPosition(), to);
- markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to)));
- markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
-
- // Start
- markerAnimator.start();
-
- return markerAnimator;
- }
-
- private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource,
- MarkerViewManager.OnMarkerViewAddedListener listener) {
- Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this)
- .fromResource(carResource);
-
- // View Markers
- return mapboxMap.addMarker(new MarkerViewOptions()
- .position(start)
- .icon(icon), listener);
-
- // GL Markers
-// return mapboxMap.addMarker(new MarkerOptions()
-// .position(start)
-// .icon(icon));
-
- }
-
- private LatLng getLatLngInBounds() {
- LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds;
- Random generator = new Random();
- double randomLat = bounds.getLatSouth() + generator.nextDouble()
- * (bounds.getLatNorth() - bounds.getLatSouth());
- double randomLon = bounds.getLonWest() + generator.nextDouble()
- * (bounds.getLonEast() - bounds.getLonWest());
- return new LatLng(randomLat, randomLon);
- }
-
- @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();
-
- stopped = true;
-
- // Stop ongoing animations, prevent memory leaks
- if (mapboxMap != null) {
- MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager();
- for (MarkerView markerView : markerViews) {
- View view = markerViewManager.getView(markerView);
- if (view != null) {
- view.animate().cancel();
- }
- }
- }
-
- // onStop
- mapView.onStop();
- mapView.removeCallbacks(animationRunnable);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- /**
- * Evaluator for LatLng pairs
- */
- private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
-
- private LatLng latLng = new LatLng();
-
- @Override
- public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
- latLng.setLatitude(startValue.getLatitude()
- + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
- latLng.setLongitude(startValue.getLongitude()
- + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
- return latLng;
- }
- }
-
- private double getBearing(LatLng from, LatLng to) {
- return TurfMeasurement.bearing(
- Position.fromCoordinates(from.getLongitude(), from.getLatitude()),
- Position.fromCoordinates(to.getLongitude(), to.getLatitude())
- );
- }
-
- private boolean isActivityStopped() {
- return stopped;
- }
-}
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..5983fb367a 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,9 @@ 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.moveCamera(
+ CameraUpdateFactory.newLatLng((LatLng) animation.getAnimatedValue()))
+ );
return latLngAnimator;
}
@@ -146,12 +126,9 @@ 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.moveCamera(
+ CameraUpdateFactory.zoomTo((Float) animation.getAnimatedValue()))
+ );
return zoomAnimator;
}
@@ -160,12 +137,9 @@ 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.moveCamera(
+ CameraUpdateFactory.bearingTo((Float) animation.getAnimatedValue()))
+ );
return bearingAnimator;
}
@@ -173,12 +147,9 @@ 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.moveCamera(
+ CameraUpdateFactory.tiltTo((Float) animation.getAnimatedValue()))
+ );
return tiltAnimator;
}
@@ -201,9 +172,12 @@ public class CameraAnimatorActivity extends AppCompatActivity implements OnMapRe
if (mapboxMap == null) {
return false;
}
- findViewById(R.id.fab).setVisibility(View.GONE);
- resetCameraPosition();
- playAnimation(item.getItemId());
+
+ if (item.getItemId() != android.R.id.home) {
+ findViewById(R.id.fab).setVisibility(View.GONE);
+ resetCameraPosition();
+ playAnimation(item.getItemId());
+ }
return super.onOptionsItemSelected(item);
}
@@ -229,12 +203,9 @@ 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.moveCamera(
+ CameraUpdateFactory.zoomTo((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/GestureDetectorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java
new file mode 100644
index 0000000000..c1698e20ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java
@@ -0,0 +1,422 @@
+package com.mapbox.mapboxsdk.testapp.activity.camera;
+
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.annotation.ColorInt;
+import android.support.annotation.IntDef;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.mapbox.android.gestures.AndroidGesturesManager;
+import com.mapbox.android.gestures.MoveGestureDetector;
+import com.mapbox.android.gestures.RotateGestureDetector;
+import com.mapbox.android.gestures.ShoveGestureDetector;
+import com.mapbox.android.gestures.StandardScaleGestureDetector;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+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 com.mapbox.mapboxsdk.testapp.utils.FontCache;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+
+import java.lang.annotation.Retention;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * Test activity showcasing APIs around gestures implementation.
+ */
+public class GestureDetectorActivity extends AppCompatActivity {
+
+ private static final int MAX_NUMBER_OF_ALERTS = 30;
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+ private RecyclerView recyclerView;
+ private GestureAlertsAdapter gestureAlertsAdapter;
+
+ private AndroidGesturesManager gesturesManager;
+
+ @Nullable
+ private Marker marker;
+ @Nullable
+ private LatLng focalPointLatLng;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_gesture_detector);
+
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap mapboxMap) {
+ GestureDetectorActivity.this.mapboxMap = mapboxMap;
+ initializeMap();
+ }
+ });
+
+ recyclerView = (RecyclerView) findViewById(R.id.alerts_recycler);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+
+ gestureAlertsAdapter = new GestureAlertsAdapter();
+ recyclerView.setAdapter(gestureAlertsAdapter);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ gestureAlertsAdapter.cancelUpdates();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ mapView.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ mapView.onStop();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ private void initializeMap() {
+ gesturesManager = mapboxMap.getGesturesManager();
+
+ RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams();
+ layoutParams.height = (int) (mapView.getHeight() / 1.75);
+ layoutParams.width = (mapView.getWidth() / 3);
+ recyclerView.setLayoutParams(layoutParams);
+
+ attachListeners();
+ }
+
+ public void attachListeners() {
+ mapboxMap.addOnMoveListener(new MapboxMap.OnMoveListener() {
+ @Override
+ public void onMoveBegin(MoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "MOVE START"));
+ }
+
+ @Override
+ public void onMove(MoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "MOVE PROGRESS"));
+ }
+
+ @Override
+ public void onMoveEnd(MoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "MOVE END"));
+ }
+ });
+
+ mapboxMap.addOnRotateListener(new MapboxMap.OnRotateListener() {
+ @Override
+ public void onRotateBegin(RotateGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "ROTATE START"));
+ }
+
+ @Override
+ public void onRotate(RotateGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "ROTATE PROGRESS"));
+ recalculateFocalPoint();
+ }
+
+ @Override
+ public void onRotateEnd(RotateGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "ROTATE END"));
+ }
+ });
+
+ mapboxMap.addOnScaleListener(new MapboxMap.OnScaleListener() {
+ @Override
+ public void onScaleBegin(StandardScaleGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SCALE START"));
+ if (focalPointLatLng != null) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "INCREASING MOVE THRESHOLD"));
+ gesturesManager.getMoveGestureDetector().setMoveThreshold(
+ ResourceUtils.convertDpToPx(GestureDetectorActivity.this, 175));
+
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "MANUALLY INTERRUPTING MOVE"));
+ gesturesManager.getMoveGestureDetector().interrupt();
+ }
+ recalculateFocalPoint();
+ }
+
+ @Override
+ public void onScale(StandardScaleGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SCALE PROGRESS"));
+ }
+
+ @Override
+ public void onScaleEnd(StandardScaleGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SCALE END"));
+
+ if (focalPointLatLng != null) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "REVERTING MOVE THRESHOLD"));
+ gesturesManager.getMoveGestureDetector().setMoveThreshold(0f);
+ }
+ }
+ });
+
+ mapboxMap.addOnShoveListener(new MapboxMap.OnShoveListener() {
+ @Override
+ public void onShoveBegin(ShoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SHOVE START"));
+ }
+
+ @Override
+ public void onShove(ShoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SHOVE PROGRESS"));
+ }
+
+ @Override
+ public void onShoveEnd(ShoveGestureDetector detector) {
+ gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SHOVE END"));
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_gestures, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ resetModes();
+ switch (item.getItemId()) {
+ case R.id.menu_gesture_none:
+ return true;
+ case R.id.menu_gesture_focus_point:
+ focalPointLatLng = new LatLng(51.50325, -0.12968);
+ marker = mapboxMap.addMarker(new MarkerOptions().position(focalPointLatLng));
+ mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(focalPointLatLng, 16));
+ mapboxMap.getUiSettings().setFocalPoint(mapboxMap.getProjection().toScreenLocation(focalPointLatLng));
+ return true;
+ case R.id.menu_gesture_animation:
+ mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(false);
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void resetModes() {
+ focalPointLatLng = null;
+ mapboxMap.getUiSettings().setFocalPoint(null);
+ gesturesManager.getMoveGestureDetector().setMoveThreshold(0f);
+ mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(true);
+
+ if (marker != null) {
+ mapboxMap.removeMarker(marker);
+ marker = null;
+ }
+ }
+
+ private void recalculateFocalPoint() {
+ if (focalPointLatLng != null) {
+ mapboxMap.getUiSettings().setFocalPoint(
+ mapboxMap.getProjection().toScreenLocation(focalPointLatLng)
+ );
+ }
+ }
+
+ private static class GestureAlertsAdapter extends RecyclerView.Adapter<GestureAlertsAdapter.ViewHolder> {
+
+ private boolean isUpdating;
+ private final Handler updateHandler = new Handler();
+ private final List<GestureAlert> alerts = new ArrayList<>();
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+
+ TextView alertMessageTv;
+
+ @ColorInt
+ public int textColor;
+
+ ViewHolder(View view) {
+ super(view);
+ Typeface typeface = FontCache.get("Roboto-Regular.ttf", view.getContext());
+ alertMessageTv = (TextView) view.findViewById(R.id.alert_message);
+ alertMessageTv.setTypeface(typeface);
+ }
+ }
+
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gesture_alert, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ GestureAlert alert = alerts.get(position);
+ holder.alertMessageTv.setText(alert.getMessage());
+ holder.alertMessageTv.setTextColor(
+ ContextCompat.getColor(holder.alertMessageTv.getContext(), alert.getColor()));
+ }
+
+ @Override
+ public int getItemCount() {
+ return alerts.size();
+ }
+
+ void addAlert(GestureAlert alert) {
+ for (GestureAlert gestureAlert : alerts) {
+ if (gestureAlert.getAlertType() != GestureAlert.TYPE_PROGRESS) {
+ break;
+ }
+
+ if (alert.getAlertType() == GestureAlert.TYPE_PROGRESS && gestureAlert.equals(alert)) {
+ return;
+ }
+ }
+
+ if (getItemCount() >= MAX_NUMBER_OF_ALERTS) {
+ alerts.remove(getItemCount() - 1);
+ }
+
+ alerts.add(0, alert);
+ if (!isUpdating) {
+ isUpdating = true;
+ updateHandler.postDelayed(updateRunnable, 250);
+ }
+ }
+
+ private Runnable updateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ notifyDataSetChanged();
+ isUpdating = false;
+ }
+ };
+
+ void cancelUpdates() {
+ updateHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ private static class GestureAlert {
+ @Retention(SOURCE)
+ @IntDef( {TYPE_NONE, TYPE_START, TYPE_PROGRESS, TYPE_END, TYPE_OTHER})
+ @interface Type {
+ }
+
+ static final int TYPE_NONE = 0;
+ static final int TYPE_START = 1;
+ static final int TYPE_END = 2;
+ static final int TYPE_PROGRESS = 3;
+ static final int TYPE_OTHER = 4;
+
+ @Type
+ private int alertType;
+
+ private String message;
+
+ @ColorInt
+ private int color;
+
+ GestureAlert(@Type int alertType, String message) {
+ this.alertType = alertType;
+ this.message = message;
+
+ switch (alertType) {
+ case TYPE_NONE:
+ color = android.R.color.black;
+ break;
+ case TYPE_END:
+ color = android.R.color.holo_red_dark;
+ break;
+ case TYPE_OTHER:
+ color = android.R.color.holo_purple;
+ break;
+ case TYPE_PROGRESS:
+ color = android.R.color.holo_orange_dark;
+ break;
+ case TYPE_START:
+ color = android.R.color.holo_green_dark;
+ break;
+ }
+ }
+
+ int getAlertType() {
+ return alertType;
+ }
+
+ String getMessage() {
+ return message;
+ }
+
+ int getColor() {
+ return color;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ GestureAlert that = (GestureAlert) o;
+
+ if (alertType != that.alertType) {
+ return false;
+ }
+ return message != null ? message.equals(that.message) : that.message == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = alertType;
+ result = 31 * result + (message != null ? message.hashCode() : 0);
+ return result;
+ }
+ }
+}
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..7685dee840 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();
}
});
}
@@ -66,11 +58,7 @@ public class CustomLayerActivity extends AppCompatActivity {
fab.setImageResource(R.drawable.ic_layers);
} else {
customLayer = new CustomLayer("custom",
- ExampleCustomLayer.createContext(),
- ExampleCustomLayer.InitializeFunction,
- ExampleCustomLayer.RenderFunction,
- ExampleCustomLayer.ContextLostFunction, // Optional
- ExampleCustomLayer.DeinitializeFunction);
+ ExampleCustomLayer.createContext());
mapboxMap.addLayerBelow(customLayer, "building");
fab.setImageResource(R.drawable.ic_layers_clear);
}
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..7953824c36 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
@@ -7,20 +7,23 @@ 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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
-import com.mapbox.mapboxsdk.style.layers.Filter;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.commons.geojson.Feature;
-import com.mapbox.services.commons.geojson.FeatureCollection;
import java.util.List;
import timber.log.Timber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
/**
@@ -41,41 +44,36 @@ 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);
+
+ Expression filter = lt(toNumber(get("height")), literal(10));
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box, filter, "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..79069a26f7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java
@@ -1,25 +1,26 @@
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;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.neq;
+
/**
* Test activity showcasing using the query source features API to query feature counts
*/
@@ -33,37 +34,29 @@ public class QuerySourceFeaturesActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_query_source_features);
- final float density = getResources().getDisplayMetrics().density;
-
// Initialize map as normal
mapView = (MapView) findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
- 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(neq(get("key1"), literal("value1"))));
+
+ // Add a click listener
+ mapboxMap.setOnMapClickListener(point -> {
+ // Query
+ List<Feature> features = source.querySourceFeatures(eq(get("key1"), literal("value1")));
+ Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features",
+ features.size()), Toast.LENGTH_SHORT).show();
+ });
});
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/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 0910045885..1ec9d48a51 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
@@ -51,18 +51,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..a165d9ab9d 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
@@ -1,12 +1,12 @@
package com.mapbox.mapboxsdk.testapp.activity.maplayout;
import android.content.Context;
-import android.location.Location;
import android.os.Bundle;
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 +35,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 +44,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 +66,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() {
@@ -158,10 +150,7 @@ public class BottomSheetActivity extends AppCompatActivity {
@Override
public void onMapReady(MapboxMap mapboxMap) {
- Location location = mapboxMap.getMyLocation();
- if (location != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
- }
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.760545, -122.436055), 15));
}
@Override
@@ -214,7 +203,6 @@ public class BottomSheetActivity extends AppCompatActivity {
public static BottomSheetFragment newInstance() {
BottomSheetFragment mapFragment = new BottomSheetFragment();
MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
- mapboxMapOptions.locationEnabled(true);
mapboxMapOptions.renderSurfaceOnTop(true);
mapboxMapOptions.styleUrl(Style.LIGHT);
mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions));
@@ -237,10 +225,7 @@ public class BottomSheetActivity extends AppCompatActivity {
@Override
public void onMapReady(MapboxMap mapboxMap) {
- Location location = mapboxMap.getMyLocation();
- if (location != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
- }
+ mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.760545, -122.436055), 15));
}
@Override
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..b4dde8d2cd 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;
@@ -12,13 +11,9 @@ import android.view.View;
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;
@@ -53,14 +48,6 @@ public class DoubleMapActivity extends AppCompatActivity {
mapboxMap = map;
mapboxMap.setStyleUrl(Style.DARK);
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18));
- try {
- mapboxMap.setMyLocationEnabled(true);
- TrackingSettings settings = mapboxMap.getTrackingSettings();
- settings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- } catch (SecurityException securityException) {
- // permission is handled in MainActivity
- finish();
- }
}
/**
@@ -90,47 +77,29 @@ 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);
+
+ 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..d547866239 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;
@@ -9,19 +8,13 @@ import android.view.MenuItem;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.camera.CameraPosition;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-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;
/**
* Test activity showcasing using the map padding API.
- * <p>
- * This activity tests for correct padding around a marker (Bangalore) and correct padding around MyLocationView.
- * </p>
*/
public class MapPaddingActivity extends AppCompatActivity {
@@ -37,19 +30,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();
});
}
@@ -101,24 +91,7 @@ public class MapPaddingActivity extends AppCompatActivity {
return true;
}
- private void toggleGps(boolean enable) {
- try {
- // Enable user location
- mapboxMap.setMyLocationEnabled(enable);
-
- TrackingSettings trackingSettings = mapboxMap.getTrackingSettings();
- trackingSettings.setDismissLocationTrackingOnGesture(false);
- trackingSettings.setMyLocationTrackingMode(
- enable ? MyLocationTracking.TRACKING_FOLLOW : MyLocationTracking.TRACKING_NONE);
- } catch (SecurityException securityException) {
- // permission not granted is handled in FeatureOverviewActivity
- finish();
- }
- }
-
private void moveToBangalore() {
- toggleGps(false);
-
LatLng bangalore = new LatLng(12.9810816, 77.6368034);
CameraPosition cameraPosition = new CameraPosition.Builder()
.zoom(16)
@@ -134,11 +107,6 @@ public class MapPaddingActivity extends AppCompatActivity {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
- case R.id.action_user_tracking:
- if (mapboxMap != null) {
- toggleGps(true);
- }
- return true;
case R.id.action_bangalore:
if (mapboxMap != null) {
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..97b4fbf6af 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java
@@ -2,17 +2,13 @@ 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.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.Property;
import com.mapbox.mapboxsdk.style.layers.PropertyFactory;
@@ -20,7 +16,9 @@ import com.mapbox.mapboxsdk.style.light.Light;
import com.mapbox.mapboxsdk.style.light.Position;
import com.mapbox.mapboxsdk.testapp.R;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
@@ -46,25 +44,22 @@ 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();
});
}
private void setupBuildings() {
FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite");
fillExtrusionLayer.setSourceLayer("building");
- fillExtrusionLayer.setFilter(eq("extrude", "true"));
+ fillExtrusionLayer.setFilter(eq(get("extrude"), literal("true")));
fillExtrusionLayer.setMinZoom(15);
fillExtrusionLayer.setProperties(
fillExtrusionColor(Color.LTGRAY),
- fillExtrusionHeight(Function.property("height", new IdentityStops<Float>())),
- fillExtrusionBase(Function.property("min_height", new IdentityStops<Float>())),
+ fillExtrusionHeight(Expression.get("height")),
+ fillExtrusionBase(Expression.get("min_height")),
fillExtrusionOpacity(0.9f)
);
mapboxMap.addLayer(fillExtrusionLayer);
@@ -73,24 +68,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..9437422d84 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java
@@ -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,7 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
@@ -25,7 +24,10 @@ import java.net.URL;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.layers.Filter.in;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.array;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.has;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
@@ -56,16 +58,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;
});
}
@@ -123,7 +121,7 @@ public class CircleLayerActivity extends AppCompatActivity implements View.OnCli
}
private void applyBusRouteFilterToBusStopSource() {
- layer.setFilter(in("number", (Object[]) Data.STOPS_FOR_ROUTE));
+ layer.setFilter(has(Expression.toString(get("number")), array(literal(Data.STOPS_FOR_ROUTE))));
}
private void addBusRouteSource() {
@@ -161,12 +159,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..dae0714d42 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java
@@ -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,18 @@ 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.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.rgb;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity;
@@ -53,22 +58,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 a parks layer
- addParksLayer();
+ // Add debug overlay
+ setupDebugZoomView();
- // 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);
@@ -152,19 +167,17 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
}
}
-
private void addExponentialZoomFunction() {
Timber.i("Add exponential zoom function");
FillLayer layer = mapboxMap.getLayerAs("water");
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 +191,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(),
+ rgba(0.0f, 255.0f, 255.0f, 1.0f),
+ stop(1, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(5, rgba(0.0f, 0.0f, 255.0f, 1.0f)),
+ stop(10, rgba(0.0f, 255.0f, 0.0f, 1.0f))
)
)
);
@@ -197,13 +209,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, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(5f, rgba(0.0f, 0.0f, 255.0f, 1.0f)),
+ stop(10f, rgba(0.0f, 255.0f, 0.0f, 1.0f))
)
)
);
@@ -217,13 +228,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"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ literal("Jordaan"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ literal("Prinseneiland"), rgba(0.0f, 255.0f, 0.0f, 1.0f),
+ rgba(0.0f, 255.0f, 255.0f, 1.0f)
+ )
)
);
@@ -236,9 +247,7 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
assert layer != null;
layer.setProperties(
fillOpacity(
- property(
- "fill-opacity",
- Stops.<Float>identity())
+ get("fill-opacity")
)
);
@@ -251,13 +260,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"),
+ rgba(0.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(0.0f, 0.0f, 255.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f))
+ )
)
);
@@ -270,16 +279,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"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(0.0f, 0.0f, 0.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 0.0f, 255.0f, 1.0f))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(255.0f, 255.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(211.0f, 211.0f, 211.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 255.0f, 255.0f, 1.0f))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(0.0f, 0.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(128.0f, 128.0f, 128.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f)))
+ )
)
)
);
@@ -288,21 +311,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"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(0.0f, 0.0f, 0.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 0.0f, 255.0f, 1.0f))
+ )),
+ stop(15, step(
+ get("stroke-width"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(255.0f, 255.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(211.0f, 211.0f, 211.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 255.0f, 255.0f, 1.0f))
+ )),
+ stop(18, step(
+ get("stroke-width"),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(1f, rgba(0.0f, 0.0f, 0.0f, 1.0f)),
+ stop(2f, rgba(128.0f, 128.0f, 128.0f, 1.0f)),
+ stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f))
))
+ )
)
);
@@ -315,30 +353,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(),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f),
+ stop(7f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(8f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(9f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(10f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(11f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(12f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(13f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(14f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ literal("Jordaan"), rgba(0.0f, 255.0f, 0.0f, 1.0f),
+ literal("PrinsenEiland"), rgba(0.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(15f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(16f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(17f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(18f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ literal("Jordaan"), rgba(0.0f, 255.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(19f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(20f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(21f, match(
+ get("name"),
+ literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
+ )),
+ stop(22f, match(
+ get("name"),
+ literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f),
+ rgba(255.0f, 255.0f, 255.0f, 1.0f)
))
+ )
)
);
@@ -359,12 +459,11 @@ public class DataDrivenStyleActivity extends AppCompatActivity {
return;
}
-
// Add a fill layer
mapboxMap.addLayer(new FillLayer(AMSTERDAM_PARKS_LAYER, source.getId())
.withProperties(
- fillColor(Color.BLACK),
- fillOutlineColor(Color.BLUE),
+ fillColor(color(Color.GREEN)),
+ fillOutlineColor(rgb(0, 0, 255)),
fillAntialias(true)
)
);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java
index 52ba8d7c7b..b7f6b10b0d 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,19 +2,21 @@ 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.Point;
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.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 java.util.Arrays;
+import java.util.List;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight;
@@ -35,58 +37,42 @@ 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
- }
- }
- });
-
- 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
- );
- }
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+ List<List<Point>> lngLats = Arrays.asList(
+ Arrays.asList(
+ Point.fromLngLat(5.12112557888031, 52.09071040847704),
+ Point.fromLngLat(5.121227502822875, 52.09053901776669),
+ Point.fromLngLat(5.121484994888306, 52.090601641371805),
+ Point.fromLngLat(5.1213884353637695, 52.090766439912635),
+ Point.fromLngLat(5.12112557888031, 52.09071040847704)
+ )
+ );
+
+ Polygon domTower = Polygon.fromLngLats(lngLats);
+
+ 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..655d4a8936 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java
@@ -10,7 +10,7 @@ import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.maps.MapView;
import com.mapbox.mapboxsdk.maps.MapboxMap;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.SymbolLayer;
import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions;
@@ -22,9 +22,12 @@ import java.net.URL;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.style.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
@@ -50,15 +53,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();
});
}
@@ -128,7 +128,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
)
);
} catch (MalformedURLException malformedUrlException) {
- Timber.e(malformedUrlException,"That's not an url... ");
+ Timber.e(malformedUrlException, "That's not an url... ");
}
// Add unclustered layer
@@ -149,10 +149,15 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
circleColor(layers[i][1]),
circleRadius(18f)
);
+
+ Expression pointCount = toNumber(get("point_count"));
circles.setFilter(
i == 0
- ? gte("point_count", layers[i][0]) :
- all(gte("point_count", layers[i][0]), lt("point_count", layers[i - 1][0]))
+ ? gte(pointCount, literal(layers[i][0])) :
+ all(
+ gte(pointCount, literal(layers[i][0])),
+ lt(pointCount, literal(layers[i - 1][0]))
+ )
);
mapboxMap.addLayer(circles);
}
@@ -160,7 +165,7 @@ public class GeoJsonClusteringActivity extends AppCompatActivity {
// Add the count labels
SymbolLayer count = new SymbolLayer("count", "earthquakes");
count.setProperties(
- textField("{point_count}"),
+ textField(get("point_count")),
textSize(12f),
textColor(Color.WHITE)
);
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/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..52509e3297
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java
@@ -0,0 +1,226 @@
+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.heatmapDensity;
+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.heatmapColor;
+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(
+
+ // 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(
+ interpolate(
+ linear(), heatmapDensity(),
+ literal(0), rgba(33, 102, 172, 0),
+ literal(0.2), rgb(103, 169, 207),
+ literal(0.4), rgb(209, 229, 240),
+ literal(0.6), rgb(253, 219, 199),
+ literal(0.8), rgb(239, 138, 98),
+ literal(1), rgb(178, 24, 43)
+ )
+ ),
+
+ // 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..f49d80d704 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,6 @@ 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,13 +38,18 @@ 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.layers.Filter.all;
-import static com.mapbox.mapboxsdk.style.layers.Filter.eq;
-import static com.mapbox.mapboxsdk.style.layers.Filter.gte;
-import static com.mapbox.mapboxsdk.style.layers.Filter.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.all;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.color;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.gte;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.lt;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.Property.FILL_TRANSLATE_ANCHOR_MAP;
import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
import static com.mapbox.mapboxsdk.style.layers.Property.SYMBOL_PLACEMENT_POINT;
@@ -85,18 +86,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);
});
}
@@ -299,7 +297,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
);
// Only show me parks (except westerpark with stroke-width == 3)
- layer.setFilter(all(eq("type", "park"), eq("stroke-width", 2)));
+ layer.setFilter(all(eq(get("type"), literal("park")), eq(get("stroke-width"), literal(3))));
mapboxMap.addLayerBelow(layer, "building");
// layer.setPaintProperty(fillColor(Color.RED)); // XXX But not after the object is attached
@@ -349,7 +347,7 @@ public class RuntimeStyleActivity extends AppCompatActivity {
);
// Only show me parks
- layer.setFilter(all(eq("type", "park")));
+ layer.setFilter(all(eq(get("type"), literal("park"))));
mapboxMap.addLayer(layer);
@@ -362,32 +360,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 +448,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 +491,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(get("name"), literal("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 +519,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(get("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 +547,27 @@ 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(toNumber(get("HRRNUM")), literal(200)),
+ lt(toNumber(get("HRRNUM")), literal(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..f32aa5faf8 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,31 +15,52 @@ 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.expressions.Expression;
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.lang.ref.WeakReference;
import java.util.HashMap;
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.eq;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.literal;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.match;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.number;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.pi;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.product;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.string;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
+import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM;
+import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_TOP;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
+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.textColor;
+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 +70,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 +92,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 +121,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(eq(get(FEATURE_RANK), literal(1)));
+ Timber.e("Filter that was set: %s", layer.getFilter());
+ return true;
}
return super.onOptionsItemSelected(item);
}
@@ -178,80 +205,140 @@ 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 WeakReference<SymbolGeneratorActivity> activity;
- LoadDataTask(MapboxMap mapboxMap, Context context) {
- this.mapboxMap = mapboxMap;
- this.context = context;
+ LoadDataTask(SymbolGeneratorActivity activity) {
+ this.activity = new WeakReference<>(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);
-
- return featureCollection;
- } catch (IOException exception) {
- return null;
+ Context context = activity.get();
+ if (context != null) {
+ try {
+ // read local geojson from raw folder
+ String tinyCountriesJson = ResourceUtils.readRawResource(context, R.raw.tiny_countries);
+ return FeatureCollection.fromJson(tinyCountriesJson);
+
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
}
+ return null;
}
-
@Override
protected void onPostExecute(FeatureCollection featureCollection) {
super.onPostExecute(featureCollection);
- if (featureCollection == null) {
+ SymbolGeneratorActivity activity = this.activity.get();
+ if (featureCollection == null || activity == null) {
return;
}
- // add a geojson to the map
- Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
- mapboxMap.addSource(source);
+ activity.onDataLoaded(featureCollection);
+ }
+ }
- // create layer use
- mapboxMap.addLayer(new SymbolLayer(LAYER_ID, SOURCE_ID)
- .withProperties(
- iconImage("{" + FEATURE_ID + "}"), // { } is a token notation
- iconAllowOverlap(false)
- )
+ public void onDataLoaded(@NonNull FeatureCollection featureCollection) {
+ // create expressions
+ Expression iconImageExpression = string(get(literal(FEATURE_ID)));
+ Expression iconSizeExpression = division(number(get(literal(FEATURE_RANK))), literal(2.0f));
+ Expression textSizeExpression = product(get(literal(FEATURE_RANK)), pi());
+ Expression textFieldExpression = concat(upcase(literal("a ")), upcase(string(get(literal(FEATURE_TYPE)))),
+ downcase(literal(" IN ")), string(get(literal(FEATURE_REGION)))
+ );
+ Expression textColorExpression = match(get(literal(FEATURE_RANK)),
+ literal(1), rgba(255, 0, 0, 1.0f),
+ literal(2), rgba(0, 0, 255.0f, 1.0f),
+ rgba(0.0f, 255.0f, 0.0f, 1.0f)
+ );
+
+ rgba(
+ division(literal(255), get(FEATURE_RANK)),
+ literal(0.0f),
+ literal(0.0f),
+ literal(1.0f)
+ );
+
+ // create symbol layer
+ SymbolLayer symbolLayer = new SymbolLayer(LAYER_ID, SOURCE_ID)
+ .withProperties(
+ // icon configuration
+ iconImage(iconImageExpression),
+ iconAllowOverlap(false),
+ iconSize(iconSizeExpression),
+ iconAnchor(ICON_ANCHOR_BOTTOM),
+ iconOffset(step(zoom(), literal(new float[] {0f, 0f}),
+ stop(1, new Float[] {0f, 0f}),
+ stop(10, new Float[] {0f, -35f})
+ )),
+
+ // text field configuration
+ textField(textFieldExpression),
+ textSize(textSizeExpression),
+ textAnchor(TEXT_ANCHOR_TOP),
+ textColor(textColorExpression)
);
- new GenerateSymbolTask(mapboxMap, context).execute(featureCollection);
- }
+ // add a geojson source to the map
+ Source source = new GeoJsonSource(SOURCE_ID, featureCollection);
+ mapboxMap.addSource(source);
+
+ // add symbol layer
+ mapboxMap.addLayer(symbolLayer);
+
+ // get expressions
+ Expression iconImageExpressionResult = symbolLayer.getIconImage().getExpression();
+ Expression iconSizeExpressionResult = symbolLayer.getIconSize().getExpression();
+ Expression textSizeExpressionResult = symbolLayer.getTextSize().getExpression();
+ Expression textFieldExpressionResult = symbolLayer.getTextField().getExpression();
+ Expression textColorExpressionResult = symbolLayer.getTextColor().getExpression();
+
+ // log expressions
+ Timber.e(iconImageExpressionResult.toString());
+ Timber.e(iconSizeExpressionResult.toString());
+ Timber.e(textSizeExpressionResult.toString());
+ Timber.e(textFieldExpressionResult.toString());
+ Timber.e(textColorExpressionResult.toString());
+
+ // reset expressions
+ symbolLayer.setProperties(
+ iconImage(iconImageExpressionResult),
+ iconSize(iconSizeExpressionResult),
+ textSize(textSizeExpressionResult),
+ textField(textFieldExpressionResult),
+ textColor(textColorExpressionResult)
+ );
+
+ new GenerateSymbolTask(mapboxMap, this).execute(featureCollection);
}
private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> {
private MapboxMap mapboxMap;
- private Context context;
+ private WeakReference<Context> context;
GenerateSymbolTask(MapboxMap mapboxMap, Context context) {
this.mapboxMap = mapboxMap;
- this.context = context;
+ this.context = new WeakReference<>(context);
}
@SuppressWarnings("WrongThread")
@Override
protected HashMap<String, Bitmap> doInBackground(FeatureCollection... params) {
- FeatureCollection featureCollection = params[0];
-
HashMap<String, Bitmap> imagesMap = new HashMap<>();
- for (Feature feature : featureCollection.getFeatures()) {
- String countryName = feature.getStringProperty(FEATURE_ID);
- TextView textView = new TextView(context);
- textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent));
- textView.setPadding(10, 5, 10, 5);
- textView.setTextColor(Color.WHITE);
- textView.setText(countryName);
- imagesMap.put(countryName, SymbolGenerator.generate(textView));
+ Context context = this.context.get();
+ List<Feature> features = params[0].features();
+ if (context != null && features != null) {
+ for (Feature feature : features) {
+ String countryName = feature.getStringProperty(FEATURE_ID);
+ TextView textView = new TextView(context);
+ textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent));
+ textView.setPadding(10, 5, 10, 5);
+ textView.setTextColor(Color.WHITE);
+ textView.setText(countryName);
+ imagesMap.put(countryName, SymbolGenerator.generate(textView));
+ }
}
return imagesMap;
}
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..81fd2c6ff8 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,34 +2,29 @@ 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 timber.log.Timber;
import java.util.List;
-import timber.log.Timber;
-
-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.interval;
+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.step;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
@@ -45,7 +40,6 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
private static final String BUS_MAKI_ICON_ID = "bus-11";
private static final String CAFE_MAKI_ICON_ID = "cafe-11";
private static final String KEY_PROPERTY_SELECTED = "selected";
- private static final float ZOOM_STOP_MIN_VALUE = 7.0f;
private static final float ZOOM_STOP_MAX_VALUE = 12.0f;
private MapView mapView;
@@ -64,14 +58,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 +84,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});
}
@@ -109,20 +98,14 @@ public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity {
layer = new SymbolLayer(LAYER_ID, SOURCE_ID);
layer.setProperties(
iconImage(
- zoom(
- interval(
- stop(ZOOM_STOP_MIN_VALUE, iconImage(BUS_MAKI_ICON_ID)),
- stop(ZOOM_STOP_MAX_VALUE, iconImage(CAFE_MAKI_ICON_ID))
- )
+ step(zoom(), literal(BUS_MAKI_ICON_ID),
+ stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID)
)
),
iconSize(
- property(
- KEY_PROPERTY_SELECTED,
- categorical(
- stop(true, iconSize(3.0f)),
- stop(false, iconSize(1.0f))
- )
+ switchCase(
+ get(KEY_PROPERTY_SELECTED), literal(3.0f),
+ literal(1.0f)
)
),
iconAllowOverlap(true)
@@ -131,19 +114,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..69bab45ce0 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));
}
});
}
@@ -104,4 +95,4 @@ public class TextureViewResizeActivity extends AppCompatActivity {
mapView.onLowMemory();
}
-}
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java
new file mode 100644
index 0000000000..15da018b0e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java
@@ -0,0 +1,94 @@
+package com.mapbox.mapboxsdk.testapp.activity.textureview;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.ImageView;
+
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.utils.ResourceUtils;
+
+import java.io.IOException;
+
+import timber.log.Timber;
+
+/**
+ * Example showcasing how to create a TextureView with a transparent background.
+ */
+public class TextureViewTransparentBackgroundActivity extends AppCompatActivity {
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_textureview_transparent);
+ setupBackground();
+ setupMapView(savedInstanceState);
+ }
+
+ private void setupBackground() {
+ ImageView imageView = (ImageView) findViewById(R.id.imageView);
+ imageView.setImageResource(R.drawable.water);
+ imageView.setScaleType(ImageView.ScaleType.FIT_XY);
+ }
+
+ private void setupMapView(Bundle savedInstanceState) {
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(map -> {
+ mapboxMap = map;
+
+ try {
+ map.setStyleJson(ResourceUtils.readRawResource(getApplicationContext(), R.raw.no_bg_style));
+ } catch (IOException exception) {
+ Timber.e(exception);
+ }
+ });
+ }
+
+ @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
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+} \ No newline at end of file
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
deleted file mode 100644
index 71b8115d2e..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/BaseLocationActivity.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.UiThread;
-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 java.util.List;
-
-/**
- * Base class for location aware activities.
- */
-public abstract class BaseLocationActivity extends AppCompatActivity implements PermissionsListener {
-
- private PermissionsManager permissionsManager;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- permissionsManager = new PermissionsManager(this);
- }
-
- @UiThread
- protected final void toggleGps(boolean enableGps) {
- if (enableGps) {
- if (!isRuntimePermissionsRequired()) {
- permissionsManager.requestLocationPermissions(this);
- } else {
- enableLocation(true);
- }
- } else {
- enableLocation(false);
- }
- }
-
- @Override
- public void onExplanationNeeded(List<String> list) {
- Snackbar.make(
- findViewById(android.R.id.content),
- TextUtils.join("", list.toArray()),
- Snackbar.LENGTH_SHORT).show();
- }
-
- @Override
- public void onPermissionResult(boolean isPermissionAccepted) {
- enableLocation(isPermissionAccepted);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
- }
-
- private boolean isRuntimePermissionsRequired() {
- return android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
- }
-
- protected abstract void enableLocation(boolean enabled);
-}
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
deleted file mode 100644
index 626f7c372d..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/CustomLocationEngineActivity.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-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;
-
-/**
- * Test activity showcasing using a custom location engine.
- */
-public class CustomLocationEngineActivity extends BaseLocationActivity {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private FloatingActionButton locationToggleFab;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_custom_location_engine);
-
- 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());
- }
- });
-
- locationToggleFab = (FloatingActionButton) findViewById(R.id.fabLocationToggle);
- locationToggleFab.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- if (mapboxMap != null) {
- enableLocation(!mapboxMap.isMyLocationEnabled());
- }
- }
- });
- }
-
- @Override
- protected void enableLocation(boolean enabled) {
- mapboxMap.setMyLocationEnabled(enabled);
- if (enabled) {
- locationToggleFab.setImageResource(R.drawable.ic_location_disabled);
- } else {
- locationToggleFab.setImageResource(R.drawable.ic_my_location);
- }
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_location_engine, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (mapboxMap != null) {
- int itemId = item.getItemId();
- if (itemId == R.id.action_id_location_source_lost) {
- mapboxMap.setLocationSource(Mapbox.getLocationEngine());
- return true;
- } else if (itemId == R.id.action_id_location_source_mock) {
- mapboxMap.setLocationSource(MockLocationEngine.getInstance());
- return true;
- } else if (itemId == R.id.action_id_location_source_null) {
- mapboxMap.setLocationSource(null);
- return true;
- }
- }
- return super.onOptionsItemSelected(item);
- }
-
- @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
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-}
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
deleted file mode 100644
index df52ccf727..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java
+++ /dev/null
@@ -1,133 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-import android.animation.AnimatorListenerAdapter;
-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 timber.log.Timber;
-
-/**
- * Sample LocationEngine that provides mocked LOCATIONS simulating GPS updates
- */
-public class MockLocationEngine extends LocationEngine {
- private static MockLocationEngine INSTANCE;
-
- private final LocationAnimator locationAnimator;
- private boolean running;
- 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());
- }
- }
- });
- }
-
- public static synchronized MockLocationEngine getInstance() {
- if (INSTANCE == null) {
- INSTANCE = new MockLocationEngine(
- MockLocationEngine.createLocation(40.416913, -3.703861),
- MockLocationEngine.createLocation(39.461643, -0.368041)
- );
- }
- return INSTANCE;
- }
-
- public static Location createLocation(double latitude, double longitude) {
- Location location = new Location(MockLocationEngine.class.getSimpleName());
- location.setLatitude(latitude);
- location.setLongitude(longitude);
- return location;
- }
-
- @Override
- public void activate() {
- // "Connection" is immediate here
- for (LocationEngineListener listener : locationListeners) {
- listener.onConnected();
- }
- }
-
- @Override
- public void deactivate() {
- }
-
- @Override
- public boolean isConnected() {
- return true; // Always connected
- }
-
- @Override
- public Location getLastLocation() {
- return null;
- }
-
- @Override
- public void requestLocationUpdates() {
- if (!running) {
- locationAnimator.start();
- running = true;
- }
- }
-
- @Override
- public void removeLocationUpdates() {
- if (running) {
- locationAnimator.stop();
- running = false;
- Timber.e("LOC %s", counter);
- }
- }
-
- @Override
- public Type obtainType() {
- return Type.MOCK;
- }
-
- private static class LocationAnimator extends AnimatorListenerAdapter {
-
- private static final long DURATION_ANIMATION = 10000;
- private final ValueAnimator locationAnimator;
- private long animationTime;
-
- LocationAnimator(Location start, Location end, ValueAnimator.AnimatorUpdateListener listener) {
- locationAnimator = ValueAnimator.ofObject(new LocationEvaluator(), start, end);
- locationAnimator.setDuration(DURATION_ANIMATION);
- locationAnimator.addUpdateListener(listener);
- locationAnimator.addListener(this);
- }
-
- void start() {
- locationAnimator.start();
- locationAnimator.setCurrentPlayTime(animationTime);
- }
-
- void stop() {
- animationTime = locationAnimator.getCurrentPlayTime();
- locationAnimator.cancel();
- }
-
- private static class LocationEvaluator implements TypeEvaluator<Location> {
-
- private Location location = new Location(MockLocationEngine.class.getSimpleName());
-
- @Override
- public Location evaluate(float fraction, Location startValue, Location endValue) {
- counter++;
- location.setLatitude(startValue.getLatitude()
- + ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
- location.setLongitude(startValue.getLongitude()
- + ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
- return location;
- }
- }
- }
-}
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
deleted file mode 100644
index 62b4f4b987..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationDrawableActivity.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-import android.graphics.Color;
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-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.MapboxMapOptions;
-import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-
-/**
- * Test activity showcasing how to change the MyLocationView drawable.
- */
-public class MyLocationDrawableActivity extends BaseLocationActivity implements LocationEngineListener {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_location_customization);
- findViewById(R.id.progress).setVisibility(View.GONE);
-
- MapboxMapOptions mapboxMapOptions = new MapboxMapOptions();
- mapboxMapOptions.styleUrl(Style.MAPBOX_STREETS);
- mapboxMapOptions.myLocationForegroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_android));
- mapboxMapOptions.myLocationBackgroundDrawable(ContextCompat.getDrawable(this, R.drawable.ic_android));
- mapboxMapOptions.myLocationForegroundTintColor(Color.GREEN);
- mapboxMapOptions.myLocationBackgroundTintColor(Color.YELLOW);
- mapboxMapOptions.myLocationBackgroundPadding(new int[] {0, 0,
- (int) getResources().getDimension(R.dimen.locationview_background_drawable_padding),
- (int) getResources().getDimension(R.dimen.locationview_background_drawable_padding)});
- mapboxMapOptions.myLocationAccuracyTint(Color.RED);
- mapboxMapOptions.myLocationAccuracyAlpha(155);
-
- mapView = new MapView(this, mapboxMapOptions);
- mapView.setId(R.id.mapView);
- ViewGroup parent = (ViewGroup) findViewById(android.R.id.content);
- parent.addView(mapView);
-
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap map) {
- mapboxMap = map;
- toggleGps(true);
- }
- });
- }
-
- @Override
- protected void enableLocation(boolean enabled) {
- mapboxMap.setMyLocationEnabled(enabled);
- }
-
- @Override
- public void onConnected() {
- // Nothing
- }
-
- @Override
- public void onLocationChanged(Location location) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 14));
- }
-
- @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
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-}
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
deleted file mode 100644
index 9dc8c1a607..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTintActivity.java
+++ /dev/null
@@ -1,197 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-import android.app.Activity;
-import android.graphics.Color;
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.view.View;
-
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-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;
-
-/**
- * Test activity showcasing how to tint the MyLocationView.
- */
-public class MyLocationTintActivity extends BaseLocationActivity implements LocationEngineListener {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private boolean firstRun;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_location_dot_color);
-
- 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);
- }
- }
- );
- }
- });
-
- }
-
- @Override
- public void onConnected() {
- // Nothing
- }
-
- @Override
- public void onLocationChanged(Location location) {
- if (mapboxMap != null && firstRun) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 15));
- firstRun = false;
- }
- }
-
- @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
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void enableLocation(boolean enabled) {
- if (enabled) {
- mapboxMap.setMyLocationEnabled(true);
- if (mapboxMap.getMyLocation() != null) {
- mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(
- new LatLng(mapboxMap.getMyLocation().getLatitude(),
- mapboxMap.getMyLocation().getLongitude()), 15));
- }
- } else {
- mapboxMap.setMyLocationEnabled(false);
- }
- }
-
- private static class ViewUtils {
-
- public static void attachClickListener(
- @NonNull Activity activity, @IdRes int buttonId, @Nullable View.OnClickListener clickListener) {
- View view = activity.findViewById(buttonId);
- if (view != null) {
- view.setOnClickListener(clickListener);
- }
- }
- }
-}
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
deleted file mode 100644
index 718c10c7cb..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationToggleActivity.java
+++ /dev/null
@@ -1,101 +0,0 @@
-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;
-
-/**
- * Test activity showcasing toggling the user location on the map.
- */
-public class MyLocationToggleActivity extends BaseLocationActivity {
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private FloatingActionButton locationToggleFab;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_location_toggle);
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(new OnMapReadyCallback() {
- @Override
- public void onMapReady(MapboxMap 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());
- }
- }
- });
- }
-
- @Override
- protected void enableLocation(boolean enabled) {
- Timber.e("Enabling location: %s", enabled);
- mapboxMap.setMyLocationEnabled(enabled);
- if (enabled) {
- locationToggleFab.setImageResource(R.drawable.ic_location_disabled);
- } else {
- locationToggleFab.setImageResource(R.drawable.ic_my_location);
- }
- }
-
- @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
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
-}
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
deleted file mode 100644
index d910a19d5d..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MyLocationTrackingModeActivity.java
+++ /dev/null
@@ -1,302 +0,0 @@
-package com.mapbox.mapboxsdk.testapp.activity.userlocation;
-
-import android.location.Location;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.Toolbar;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Spinner;
-import android.widget.Toast;
-
-import com.mapbox.mapboxsdk.Mapbox;
-import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
-import com.mapbox.mapboxsdk.constants.MyBearingTracking;
-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.UiSettings;
-import com.mapbox.mapboxsdk.testapp.R;
-import com.mapbox.services.android.telemetry.location.LocationEngineListener;
-
-import timber.log.Timber;
-
-/**
- * Test activity showcasing the different tracking modes the SDK exposes.
- * <p>
- * This includes MyLocationTracking/MyLocationBearingTracking and how the components can be configured to be dismissed
- * using gesture configurations.
- * </p>
- */
-public class MyLocationTrackingModeActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener,
- OnMapReadyCallback, LocationEngineListener {
-
- public static final int TRACKING_NONE_INDEX = 0;
- public static final int TRACKING_FOLLOW_INDEX = 1;
- public static final int BEARING_NONE_INDEX = 0;
- public static final int BEARING_GPS_INDEX = 1;
- public static final int BEARING_COMPASS_INDEX = 2;
-
- private MapView mapView;
- private MapboxMap mapboxMap;
- private Spinner locationSpinner;
- private Spinner bearingSpinner;
- private boolean firstRun = true;
-
- private MenuItem dismissLocationTrackingOnGestureItem;
- private MenuItem dismissBearingTrackingOnGestureItem;
- private MenuItem enableRotateGesturesItem;
- private MenuItem enableScrollGesturesItem;
-
- @Override
- protected void onCreate(final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_my_location_tracking);
- setupToolbar();
-
- mapView = (MapView) findViewById(R.id.mapView);
- mapView.onCreate(savedInstanceState);
- mapView.getMapAsync(this);
- }
-
- @Override
- public void onMapReady(MapboxMap mapboxMap) {
- MyLocationTrackingModeActivity.this.mapboxMap = mapboxMap;
-
- mapboxMap.setMyLocationEnabled(true);
- Mapbox.getLocationEngine().addLocationEngineListener(this);
- Mapbox.getLocationEngine().requestLocationUpdates();
- }
-
- @Override
- public void onConnected() {
- // Nothing
- }
-
- @Override
- public void onLocationChanged(Location location) {
- Timber.e("Location changed %s", location);
- if (firstRun) {
- setInitialLocation(location, 16);
- }
- }
-
- private void setInitialLocation(Location location, double zoom) {
- mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), zoom));
- mapboxMap.setMyLocationEnabled(true);
- setupSpinners(mapboxMap);
- firstRun = false;
- }
-
- private void setupToolbar() {
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
-
- final ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayShowTitleEnabled(false);
- actionBar.setDisplayHomeAsUpEnabled(true);
- actionBar.setDisplayShowHomeEnabled(true);
-
- locationSpinner = (Spinner) findViewById(R.id.spinner_location);
- ArrayAdapter<CharSequence> locationTrackingAdapter = ArrayAdapter.createFromResource(
- actionBar.getThemedContext(), R.array.user_tracking_mode, android.R.layout.simple_spinner_item);
- locationTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- locationSpinner.setAdapter(locationTrackingAdapter);
-
- bearingSpinner = (Spinner) findViewById(R.id.spinner_bearing);
- ArrayAdapter<CharSequence> bearingTrackingAdapter = ArrayAdapter.createFromResource(
- actionBar.getThemedContext(), R.array.user_bearing_mode, android.R.layout.simple_spinner_item);
- bearingTrackingAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- bearingSpinner.setAdapter(bearingTrackingAdapter);
- }
- }
-
- private void setupSpinners(@NonNull MapboxMap mapboxMap) {
- locationSpinner.setOnItemSelectedListener(MyLocationTrackingModeActivity.this);
- 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.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);
- }
- });
- }
-
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) throws SecurityException {
- TrackingSettings trackingSettings = mapboxMap.getTrackingSettings();
- if (parent.getId() == R.id.spinner_location) {
- switch (position) {
- case TRACKING_NONE_INDEX:
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
- break;
-
- case TRACKING_FOLLOW_INDEX:
- trackingSettings.setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
- break;
- }
- } else if (parent.getId() == R.id.spinner_bearing) {
- switch (position) {
- case BEARING_NONE_INDEX:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.NONE);
- break;
-
- case BEARING_GPS_INDEX:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.GPS);
- break;
-
- case BEARING_COMPASS_INDEX:
- trackingSettings.setMyBearingTrackingMode(MyBearingTracking.COMPASS);
- break;
- }
- }
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
-
- @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();
- Mapbox.getLocationEngine().removeLocationEngineListener(this);
- Mapbox.getLocationEngine().removeLocationUpdates();
- mapView.onStop();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mapView.onSaveInstanceState(outState);
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mapView.onDestroy();
- }
-
- @Override
- public void onLowMemory() {
- super.onLowMemory();
- mapView.onLowMemory();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_tracking, menu);
- dismissLocationTrackingOnGestureItem = menu.findItem(R.id.action_toggle_dismissible_location);
- dismissBearingTrackingOnGestureItem = menu.findItem(R.id.action_toggle_dismissible_bearing);
- enableRotateGesturesItem = menu.findItem(R.id.action_toggle_rotate_gesture_enabled);
- enableScrollGesturesItem = menu.findItem(R.id.action_toggle_scroll_gesture_enabled);
- setCheckBoxes();
- return true;
- }
-
- private void setCheckBoxes() {
- if (mapboxMap != null && dismissBearingTrackingOnGestureItem != null) {
- TrackingSettings trackingSettings = mapboxMap.getTrackingSettings();
- UiSettings uiSettings = mapboxMap.getUiSettings();
- dismissBearingTrackingOnGestureItem.setChecked(trackingSettings.isDismissBearingTrackingOnGesture());
- dismissLocationTrackingOnGestureItem.setChecked(trackingSettings.isDismissLocationTrackingOnGesture());
- enableRotateGesturesItem.setChecked(uiSettings.isRotateGesturesEnabled());
- enableScrollGesturesItem.setChecked(uiSettings.isScrollGesturesEnabled());
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- boolean state;
- switch (item.getItemId()) {
- case android.R.id.home:
- onBackPressed();
- return true;
- case R.id.action_toggle_dismissible_location:
- state = !item.isChecked();
- mapboxMap.getTrackingSettings().setDismissLocationTrackingOnGesture(state);
- Toast.makeText(this, "Dismiss tracking mode on gesture = " + state, Toast.LENGTH_SHORT).show();
- item.setChecked(state);
- return true;
- case R.id.action_toggle_dismissible_bearing:
- state = !item.isChecked();
- mapboxMap.getTrackingSettings().setDismissBearingTrackingOnGesture(state);
- Toast.makeText(this, "Dismiss bearing mode on gesture = " + state, Toast.LENGTH_SHORT).show();
- item.setChecked(state);
- return true;
- case R.id.action_toggle_rotate_gesture_enabled:
- state = !item.isChecked();
- mapboxMap.getUiSettings().setRotateGesturesEnabled(state);
- Toast.makeText(this, "Rotate gesture enabled = " + state, Toast.LENGTH_SHORT).show();
- item.setChecked(state);
- return true;
- case R.id.action_toggle_scroll_gesture_enabled:
- state = !item.isChecked();
- mapboxMap.getUiSettings().setScrollGesturesEnabled(state);
- Toast.makeText(this, "Scroll gesture enabled = " + state, Toast.LENGTH_SHORT).show();
- item.setChecked(state);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
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/activity/Feature.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java
index d745982388..f3562b5b15 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java
@@ -9,14 +9,12 @@ public class Feature implements Parcelable {
private String label;
private String description;
private String category;
- private boolean requiresLocationPermission;
- public Feature(String name, String label, String description, String category, boolean requiresLocationPermission) {
+ public Feature(String name, String label, String description, String category) {
this.name = name;
this.label = label;
this.description = description;
this.category = category;
- this.requiresLocationPermission = requiresLocationPermission;
}
private Feature(Parcel in) {
@@ -24,7 +22,6 @@ public class Feature implements Parcelable {
label = in.readString();
description = in.readString();
category = in.readString();
- requiresLocationPermission = in.readByte() != 0;
}
public String getName() {
@@ -48,10 +45,6 @@ public class Feature implements Parcelable {
return category;
}
- public boolean isRequiresLocationPermission() {
- return requiresLocationPermission;
- }
-
public int describeContents() {
return 0;
}
@@ -61,7 +54,6 @@ public class Feature implements Parcelable {
out.writeString(label);
out.writeString(description);
out.writeString(category);
- out.writeByte((byte) (requiresLocationPermission ? 1 : 0));
}
public static final Parcelable.Creator<Feature> CREATOR
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/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
index f0cca57e10..6b522ac210 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.testapp.utils;
import android.content.Context;
import android.support.annotation.RawRes;
+import android.util.TypedValue;
import java.io.BufferedReader;
import java.io.IOException;
@@ -16,21 +17,23 @@ public class ResourceUtils {
public static String readRawResource(Context context, @RawRes int rawResource) throws IOException {
String json = "";
if (context != null) {
- InputStream is = context.getResources().openRawResource(rawResource);
Writer writer = new StringWriter();
char[] buffer = new char[1024];
- try {
+ try (InputStream is = context.getResources().openRawResource(rawResource)) {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int numRead;
while ((numRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, numRead);
}
- } finally {
- is.close();
}
json = writer.toString();
}
return json;
}
+
+ public static float convertDpToPx(Context context, float dp) {
+ return TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics());
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg
new file mode 100644
index 0000000000..71b758b490
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg
Binary files differ
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled.xml
deleted file mode 100644
index 4fedff778b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFF"
- android:pathData="M20.94,11c-0.46,-4.17 -3.77,-7.48 -7.94,-7.94L13,1h-2v2.06c-1.13,0.12 -2.19,0.46 -3.16,0.97l1.5,1.5C10.16,5.19 11.06,5 12,5c3.87,0 7,3.13 7,7 0,0.94 -0.19,1.84 -0.52,2.65l1.5,1.5c0.5,-0.96 0.84,-2.02 0.97,-3.15L23,13v-2h-2.06zM3,4.27l2.04,2.04C3.97,7.62 3.25,9.23 3.06,11L1,11v2h2.06c0.46,4.17 3.77,7.48 7.94,7.94L11,23h2v-2.06c1.77,-0.2 3.38,-0.91 4.69,-1.98L19.73,21 21,19.73 4.27,3 3,4.27zM16.27,17.54C15.09,18.45 13.61,19 12,19c-3.87,0 -7,-3.13 -7,-7 0,-1.61 0.55,-3.09 1.46,-4.27l9.81,9.81z"/>
-</vector>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
index 0566757d58..252af714e7 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml
@@ -10,9 +10,9 @@
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:mapbox_cameraTargetLat="51.502615"
- app:mapbox_cameraTargetLng="4.972326"
- app:mapbox_cameraZoom="6"
+ app:mapbox_cameraTargetLat="38.90962"
+ app:mapbox_cameraTargetLng="-77.04341"
+ app:mapbox_cameraZoom="15"
app:mapbox_styleUrl="@string/mapbox_style_light"/>
</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml
deleted file mode 100644
index e9f461c7ee..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_location_engine.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@id/coordinator_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:mapbox_cameraTargetLat="40.416872"
- app:mapbox_cameraTargetLng="-3.703807"
- app:mapbox_cameraZoom="4"/>
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabLocationToggle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/fab_margin"
- android:src="@drawable/ic_my_location"
- tools:backgroundTint="@color/primary"/>
-
-</android.support.design.widget.CoordinatorLayout>
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_gesture_detector.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml
new file mode 100644
index 0000000000..26c61e1639
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml
@@ -0,0 +1,26 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.mapbox.mapboxsdk.testapp.activity.camera.GestureDetectorActivity">
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="51.50325"
+ app:mapbox_cameraTargetLng="-0.11968"
+ app:mapbox_cameraZoom="15" />
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/alerts_recycler"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentTop="true"
+ android:background="#97cdcfd3" />
+
+</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/layout/activity_my_location_customization.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
deleted file mode 100644
index addfe8427b..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_customization.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
- <android.support.v4.widget.ContentLoadingProgressBar
- android:id="@id/progress"
- style="?android:attr/progressBarStyleLarge"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"/>
-
-</merge>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml
deleted file mode 100644
index de18e265de..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_dot_color.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:mapbox="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- mapbox:mapbox_uiAttribution="false"
- mapbox:mapbox_uiLogo="false"/>
-
- <LinearLayout
- style="?android:attr/buttonBarStyle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:background="@color/accent"
- android:orientation="horizontal"
- android:weightSum="4">
-
- <Button
- android:id="@+id/default_user_dot_coloring_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/button_user_dot_default"
- android:textColor="@color/white"/>
-
- <Button
- android:id="@+id/tint_user_dot_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/button_user_dot_tint"
- android:textColor="@color/white"/>
-
- <Button
- android:id="@+id/user_accuracy_ring_tint_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/button_user_accuracy_ring_tint"
- android:textColor="@color/white"/>
-
- <Button
- android:id="@+id/user_dot_transparent_button"
- style="?android:attr/buttonBarButtonStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/button_user_transparent_tint"
- android:textColor="@color/white"/>
-
- </LinearLayout>
-
-</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml
deleted file mode 100644
index 2ec35faf04..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_toggle.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.design.widget.CoordinatorLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@id/coordinator_layout"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
- <android.support.design.widget.FloatingActionButton
- android:id="@+id/fabLocationToggle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="end|bottom"
- android:layout_margin="@dimen/fab_margin"
- android:src="@drawable/ic_my_location"
- tools:backgroundTint="@color/primary"/>
-
-</android.support.design.widget.CoordinatorLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml
deleted file mode 100644
index 7236a944e9..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_my_location_tracking.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout 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">
-
- <android.support.v7.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:background="@color/primary"
- android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:weightSum="2">
-
- <Spinner
- android:id="@+id/spinner_location"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:layout_marginRight="8dp"
- android:layout_weight="1" />
-
- <Spinner
- android:id="@+id/spinner_bearing"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
- android:layout_weight="1" />
-
- </LinearLayout>
-
-
- </android.support.v7.widget.Toolbar>
-
- <com.mapbox.mapboxsdk.maps.MapView
- android:id="@+id/mapView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- app:mapbox_myLocationTintColor="@color/primary"
- app:mapbox_myLocationAccuracyTintColor="@color/primary"
- app:mapbox_styleUrl="@string/mapbox_style_mapbox_streets"
- app:mapbox_cameraZoom="8" />
-
-</LinearLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml
index 2baa3d39dd..b00076e779 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml
@@ -7,7 +7,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
-
+
<com.mapbox.mapboxsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml
new file mode 100644
index 0000000000..3b9ee71359
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/coordinator_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@null"/>
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:mapbox_cameraTargetLat="48.507879"
+ app:mapbox_cameraTargetLng="8.363795"
+ app:mapbox_cameraZoom="2"
+ app:mapbox_renderTextureMode="true"
+ app:mapbox_renderTextureTranslucentSurface="true"
+ app:mapbox_styleUrl="mapbox://styles/agerace-globant/cja02de7193b02suvy4gpbt2c"/>
+
+</android.support.design.widget.CoordinatorLayout>
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_gesture_alert.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_gesture_alert.xml
new file mode 100644
index 0000000000..5fc17d049f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_gesture_alert.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/alert_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="2dp"
+ android:paddingTop="2dp"
+ android:textSize="9sp" />
+
+</FrameLayout> \ No newline at end of file
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/menu/menu_gestures.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml
new file mode 100644
index 0000000000..8b3a540ffa
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:id="@+id/menu_gesture_none"
+ android:title="None" />
+ <item
+ android:id="@+id/menu_gesture_focus_point"
+ android:title="Focus on a point" />
+ <item
+ android:id="@+id/menu_gesture_animation"
+ android:title="Turn off velocity animations" />
+</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml
deleted file mode 100644
index dd7408df09..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_engine.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:mapbox="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/action_id_location_source_lost"
- android:title="@string/menuitem_title_change_location_source_lost"
- mapbox:showAsAction="never"/>
-
- <item
- android:id="@+id/action_id_location_source_mock"
- android:title="@string/menuitem_title_change_location_source_mock"
- mapbox:showAsAction="never"/>
-
- <item
- android:id="@+id/action_id_location_source_null"
- android:title="@string/menuitem_title_change_location_source_null"
- mapbox:showAsAction="never"/>
-
-</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
index 7132c0c2a9..f0197a9716 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml
@@ -2,10 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mapbox="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/action_user_tracking"
- android:title="@string/my_location_tracking"
- mapbox:showAsAction="never" />
- <item
android:id="@+id/action_bangalore"
android:title="@string/bangalore"
mapbox:showAsAction="never" />
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml
deleted file mode 100644
index 940dd9c461..0000000000
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_tracking.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
-
- <group android:checkableBehavior="all">
- <item
- android:id="@+id/action_toggle_dismissible_location"
- android:checkable="true"
- android:checked="false"
- android:title="@string/menuitem_title_tracking_mode_dismiss_on_gesture"
- app:showAsAction="never" />
- <item
- android:id="@+id/action_toggle_dismissible_bearing"
- android:checkable="true"
- android:checked="false"
- android:title="@string/menuitem_title_bearing_mode_dismiss_on_gesture"
- app:showAsAction="never" />
- <item
- android:id="@+id/action_toggle_rotate_gesture_enabled"
- android:checkable="true"
- android:checked="false"
- android:title="@string/menuitem_title_rotate_gesture_enabled"
- app:showAsAction="never" />
- <item
- android:id="@+id/action_toggle_scroll_gesture_enabled"
- android:checkable="true"
- android:checked="false"
- android:title="@string/menuitem_title_scroll_gesture_enabled"
- app:showAsAction="never" />
- </group>
-
-</menu>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json
new file mode 100644
index 0000000000..964eefc319
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json
@@ -0,0 +1,43 @@
+{
+ "version": 8,
+ "name": "Land",
+ "metadata": {
+ "mapbox:autocomposite": true,
+ },
+ "sources": {
+ "composite": {
+ "url": "mapbox://mapbox.mapbox-terrain-v2",
+ "type": "vector"
+ }
+ },
+ "sprite": "mapbox://sprites/mapbox/mapbox-terrain-v2",
+ "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
+ "layers": [
+ {
+ "layout": {
+ "visibility": "visible"
+ },
+ "type": "fill",
+ "source": "composite",
+ "id": "admin",
+ "paint": {
+ "fill-color": "hsl(359, 100%, 50%)",
+ "fill-opacity": 1
+ },
+ "source-layer": "landcover"
+ },
+ {
+ "layout": {
+ "visibility": "visible"
+ },
+ "type": "fill",
+ "source": "composite",
+ "id": "layer-0",
+ "paint": {
+ "fill-opacity": 1,
+ "fill-color": "hsl(359, 100%, 50%)"
+ },
+ "source-layer": "Layer_0"
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
index 5c0828ab74..04d2e8d56e 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml
@@ -2,15 +2,9 @@
<resources>
<string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
<string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string>
- <string name="menuitem_title_tracking_mode_dismiss_on_gesture">Dismiss location tracking on gesture</string>
- <string name="menuitem_title_bearing_mode_dismiss_on_gesture">Dismiss bearing tracking on gesture</string>
<string name="menuitem_title_reset">Reset</string>
- <string name="menuitem_title_rotate_gesture_enabled">Enable rotate gestures</string>
- <string name="menuitem_title_scroll_gesture_enabled">Enable scroll gestures</string>
- <string name="menuitem_title_change_location_source_lost">Change to LOST location source</string>
- <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>
@@ -20,10 +14,6 @@
<string name="button_camera_move">Move</string>
<string name="button_camera_ease">Ease</string>
<string name="button_camera_animate">Animate</string>
- <string name="button_user_dot_default">Default</string>
- <string name="button_user_dot_tint">Tint dot</string>
- <string name="button_user_accuracy_ring_tint">Tint ring</string>
- <string name="button_user_transparent_tint">tran</string>
<string name="button_open_dialog">Open dialog</string>
<string name="button_download_region">Download region</string>
<string name="button_list_regions">List regions</string>
@@ -72,7 +62,6 @@
<string name="add_a_composite_categorical_function">Add a composite, categorical function</string>
<string name="add_a_composite_exponential_function">Add a composite, exponential function</string>
<string name="add_a_composite_interval_function">Add a composite, interval function</string>
- <string name="my_location_tracking">My Location Tracking</string>
<string name="bangalore">Bangalore</string>
<string name="list_all_layers_in_the_style">List all layers in the style</string>
<string name="list_all_sources_in_the_style">List all sources in the style</string>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
index 94763342d2..4b7ded8e3a 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml
@@ -7,16 +7,4 @@
<item>1000</item>
<item>10000</item>
</string-array>
-
- <string-array name="user_tracking_mode">
- <item>Disabled</item>
- <item>Follow tracking</item>
- </string-array>
-
- <string-array name="user_bearing_mode">
- <item>Disabled</item>
- <item>GPS bearing</item>
- <item>Compass bearing</item>
- <!--<item>Combined mode</item>-->
- </string-array>
</resources> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
index dbc6b59db6..aafeb1cd9c 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml
@@ -10,7 +10,6 @@
<string name="category_infowindow">Info Window</string>
<string name="category_maplayout">Map Layout</string>
<string name="category_offline">Offline</string>
- <string name="category_userlocation">User Location</string>
<string name="category_style">Styling</string>
<string name="category_features">Features</string>
<string name="category_storage">Storage</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..17d6ad57c6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml
@@ -1,10 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="description_user_location_tracking">Tracks the location of the user</string>
- <string name="description_user_location_customization">Customize the location of the user</string>
- <string name="description_user_location_dot_color">Customize the user location color</string>
- <string name="description_user_location_toggle">Toggle location of the user on and off</string>
- <string name="description_custom_location_engine">Customize location engine</string>
<string name="description_custom_layer">Overlay a custom native layer on the map</string>
<string name="description_info_window_adapter">Learn how to create a custom InfoWindow</string>
<string name="description_cameraposition">CameraPosition capabilities</string>
@@ -24,7 +19,7 @@
<string name="description_offline">Offline Map example</string>
<string name="description_update_metadata">Update metadata example</string>
<string name="description_offline_region_delete">Delete region example</string>
- <string name="description_animated_marker">Animate the position change of a marker</string>
+ <string name="description_animated_symbollayer">Animate the position change of a symbol layer</string>
<string name="description_polyline">Add a polyline to a map</string>
<string name="description_polygon">Add a polygon to a map</string>
<string name="description_scroll_by">Scroll with pixels in x,y direction</string>
@@ -43,7 +38,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 +62,10 @@
<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_textureview_transparent">Enable a transparent surface on TextureView</string>
+ <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>
+ <string name="description_gesture_detector">Manipulate gestures detector\'s settings</string>
+</resources>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index 0a43af09de..9d34183435 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -6,6 +6,5 @@
<dimen name="map_padding_bottom">256dp</dimen>
<dimen name="map_padding_right">32dp</dimen>
<dimen name="map_padding_top">0dp</dimen>
- <dimen name="locationview_background_drawable_padding">2dp</dimen>
<dimen name="navigation_drawer_width">240dp</dimen>
</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..3f011bd3ed 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml
@@ -4,7 +4,7 @@
<string name="activity_map_fragment">Map Fragment</string>
<string name="activity_multimap">Multiple Maps on Screen</string>
<string name="activity_add_bulk_markers">Add Markers In Bulk</string>
- <string name="activity_animated_marker">Animated Markers</string>
+ <string name="activity_animated_symbollayer">Animated SymbolLayer</string>
<string name="activity_dynamic_marker">Dynamic Marker</string>
<string name="activity_polyline">Polyline</string>
<string name="activity_polygon">Polygon</string>
@@ -21,11 +21,6 @@
<string name="activity_scroll_by">Scroll By Method</string>
<string name="activity_double_map">Double Map Activity</string>
<string name="activity_snapshot">Snapshot Activity</string>
- <string name="activity_user_tracking_mode">User tracking mode</string>
- <string name="activity_user_tracking_customization">User location drawable</string>
- <string name="activity_user_dot_color">User location tint color</string>
- <string name="activity_user_location_toggle">User location toggle</string>
- <string name="activity_custom_location_engine">Custom location engine</string>
<string name="activity_custom_layer">Custom Layer</string>
<string name="activity_map_padding">Map Padding</string>
<string name="activity_debug_mode">Debug Mode</string>
@@ -67,6 +62,10 @@
<string name="activity_textureview_debug">TextureView debug</string>
<string name="activity_textureview_resize">TextureView resize</string>
<string name="activity_textureview_animate">TextureView animation</string>
+ <string name="activity_textureview_transparent">TextureView transparent background</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>
+ <string name="activity_gesture_detector">Gestures detector</string>
</resources> \ No newline at end of file
diff --git a/platform/android/README.md b/platform/android/README.md
index 3787ff85fe..31e0a94f2c 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.
@@ -12,7 +12,7 @@ Alright. So, actually, you may be in the wrong place. From here on in, this READ
**To install and use the Mapbox Maps SDK for Android in an application, see the [Mapbox Maps SDK for Android website](https://www.mapbox.com/install/android/).**
-[![](https://www.mapbox.com/android-sdk/images/splash.png)](https://www.mapbox.com/android-sdk/)
+[![](https://www.mapbox.com/android-docs/assets/overview-map-sdk-322-9abe118316efb5910b6101e222a2e57c.png)](https://www.mapbox.com/android-sdk/)
### Setup environment
@@ -23,79 +23,91 @@ 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, the `5.2.0-SNAPSHOT` would look like:
-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
+implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.2.0-SNAPSHOT'
+```
+
+You also 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..77074dc82c 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,16 +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/functions/stop.cpp
- platform/android/src/style/functions/stop.hpp
- platform/android/src/style/functions/categorical_stops.cpp
- platform/android/src/style/functions/categorical_stops.hpp
- platform/android/src/style/functions/exponential_stops.cpp
- platform/android/src/style/functions/exponential_stops.hpp
- platform/android/src/style/functions/identity_stops.cpp
- platform/android/src/style/functions/identity_stops.hpp
- platform/android/src/style/functions/interval_stops.cpp
- platform/android/src/style/functions/interval_stops.hpp
+ platform/android/src/style/sources/raster_dem_source.cpp
+ platform/android/src/style/sources/raster_dem_source.hpp
platform/android/src/style/position.cpp
platform/android/src/style/position.hpp
platform/android/src/style/light.cpp
@@ -224,6 +212,8 @@ add_library(mbgl-android STATIC
platform/android/src/map_renderer_runnable.hpp
# Java core classes
+ platform/android/src/java/lang.cpp
+ platform/android/src/java/lang.hpp
platform/android/src/java/util.cpp
platform/android/src/java/util.hpp
@@ -240,6 +230,8 @@ add_library(mbgl-android STATIC
platform/android/src/geojson/feature_collection.hpp
platform/android/src/geojson/geometry.cpp
platform/android/src/geojson/geometry.hpp
+ platform/android/src/geojson/geometry_collection.cpp
+ platform/android/src/geojson/geometry_collection.hpp
platform/android/src/geojson/line_string.cpp
platform/android/src/geojson/line_string.hpp
platform/android/src/geojson/multi_line_string.cpp
@@ -252,8 +244,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 +302,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 +315,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 +326,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 +343,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 f3ec2f5a25..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.10"
- 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..07a9938915
--- /dev/null
+++ b/platform/android/gradle/dependencies.gradle
@@ -0,0 +1,58 @@
+ext {
+
+ androidVersions = [
+ minSdkVersion : 14,
+ targetSdkVersion : 25,
+ compileSdkVersion: 25,
+ buildToolsVersion: '26.0.3'
+ ]
+
+ versions = [
+ mapboxServices : '3.0.1',
+ mapboxTelemetry: '3.1.0',
+ mapboxGestures : '0.2.0',
+ 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.10.0'
+ ]
+
+ dependenciesList = [
+ mapboxJavaServices : "com.mapbox.mapboxsdk:mapbox-sdk-services:${versions.mapboxServices}",
+ mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}",
+ mapboxAndroidTelemetry: "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}",
+ mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}",
+
+ // 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}",
+ testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${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}"
+ ]
+}
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/gradle/gradle-dependencies-graph.gradle b/platform/android/gradle/gradle-dependencies-graph.gradle
new file mode 100644
index 0000000000..5cbc7b974f
--- /dev/null
+++ b/platform/android/gradle/gradle-dependencies-graph.gradle
@@ -0,0 +1,29 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.3.0"
+ }
+}
+
+import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin
+import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator
+import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions
+import com.vanniktech.dependency.graph.generator.dot.Color
+import com.vanniktech.dependency.graph.generator.dot.Shape
+import com.vanniktech.dependency.graph.generator.dot.Style
+
+plugins.apply(DependencyGraphGeneratorPlugin)
+
+def mapboxGenerator = new Generator(
+ "mapboxLibraries", // Suffix for our Gradle task.
+ "", // Root suffix that we don't want in this case.
+ { dependency -> dependency.getModuleGroup().startsWith("com.mapbox.mapboxsdk") }, // Only want Mapbox libs.
+ { dependency -> true }, // Include transitive dependencies.
+)
+
+dependencyGraphGenerator {
+ generators = [mapboxGenerator]
+}
diff --git a/platform/android/gradle/gradle-javadoc.gradle b/platform/android/gradle/gradle-javadoc.gradle
new file mode 100644
index 0000000000..cf7f8f743b
--- /dev/null
+++ b/platform/android/gradle/gradle-javadoc.gradle
@@ -0,0 +1,18 @@
+android.libraryVariants.all { variant ->
+ def name = variant.name
+ task "javadoc$name"(type: Javadoc) {
+ description = "Generates javadoc for build $name"
+ failOnError = false
+ destinationDir = new File(destinationDir, variant.baseName)
+ source = variant.javaCompile.source
+ options.windowTitle("Mapbox Maps SDK for Android $VERSION_NAME Reference")
+ options.docTitle("Mapbox Maps SDK for Android $VERSION_NAME")
+ options.header("Mapbox Maps SDK for Android $VERSION_NAME Reference")
+ 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")
+ options.group("Mapbox Android SDK", "com.mapbox.*")
+ exclude '**/R.java', '**/BuildConfig.java'
+ }
+}
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..157808aa51 100644
--- a/platform/android/scripts/exclude-activity-gen.json
+++ b/platform/android/scripts/exclude-activity-gen.json
@@ -18,7 +18,7 @@
"LocationPickerActivity",
"GeoJsonClusteringActivity",
"RuntimeStyleTestActivity",
- "AnimatedMarkerActivity",
+ "AnimatedSymbolLayerActivity",
"ViewPagerActivity",
"MapFragmentActivity",
"SupportMapFragmentActivity",
@@ -27,5 +27,11 @@
"QueryRenderedFeaturesBoxHighlightActivity",
"MultiMapActivity",
"MapInDialogActivity",
+ "ManualZoomActivity",
+ "MaxMinZoomActivity",
+ "ScrollByActivity",
+ "ZoomFunctionSymbolLayerActivity",
+ "SymbolGeneratorActivity",
+ "TextureViewTransparentBackgroundActivity",
"SimpleMapActivity"
-] \ 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..3b0363cc19 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');
@@ -12,6 +13,7 @@ const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => {
var property = spec[`light`][name];
property.name = name;
property['light-property'] = true;
+ property.doc = property.doc.replace(/°/g,'&#xB0;');
memo.push(property);
return memo;
}, []);
@@ -147,6 +149,24 @@ global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
}
};
+global.defaultExpressionJava = function(property) {
+ switch (property.type) {
+ case 'boolean':
+ return 'boolean';
+ case 'number':
+ return 'number';
+ case 'string':
+ return "string";
+ case 'enum':
+ return "string";
+ case 'color':
+ return 'toColor';
+ case 'array':
+ return "array";
+ default: return "string";
+ }
+}
+
global.defaultValueJava = function(property) {
if(property.name.endsWith("-pattern")) {
return '"pedestrian-polygon"';
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/bitmap.cpp b/platform/android/src/bitmap.cpp
index 50088116f4..46e7253050 100644
--- a/platform/android/src/bitmap.cpp
+++ b/platform/android/src/bitmap.cpp
@@ -110,8 +110,7 @@ PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap
}
if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {
- // TODO: convert
- throw std::runtime_error("bitmap decoding: bitmap format invalid");
+ bitmap = Bitmap::Copy(env, bitmap);
}
const PixelGuard guard(env, bitmap);
@@ -128,5 +127,12 @@ PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, jni::Object<Bitmap> bitmap
return { Size{ info.width, info.height }, std::move(pixels) };
}
+jni::Object<Bitmap> Bitmap::Copy(jni::JNIEnv& env, jni::Object<Bitmap> bitmap) {
+ using Signature = jni::Object<Bitmap>(jni::Object<Config>, jni::jboolean);
+ auto static method = _class.GetMethod<Signature>(env, "copy");
+ auto config = Bitmap::Config::Create(env, Bitmap::Config::Value::ARGB_8888);
+ return bitmap.Call(env, method, config, (jni::jboolean) false);
+}
+
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/bitmap.hpp b/platform/android/src/bitmap.hpp
index f64f42ae87..c4e41db1e0 100644
--- a/platform/android/src/bitmap.hpp
+++ b/platform/android/src/bitmap.hpp
@@ -43,6 +43,7 @@ public:
static PremultipliedImage GetImage(jni::JNIEnv&, jni::Object<Bitmap>);
static jni::Object<Bitmap> CreateBitmap(jni::JNIEnv&, const PremultipliedImage&);
+ static jni::Object<Bitmap> Copy(jni::JNIEnv&, jni::Object<Bitmap>);
private:
static jni::Class<Bitmap> _class;
diff --git a/platform/android/src/example_custom_layer.cpp b/platform/android/src/example_custom_layer.cpp
index be8e830412..e805532541 100644
--- a/platform/android/src/example_custom_layer.cpp
+++ b/platform/android/src/example_custom_layer.cpp
@@ -112,18 +112,9 @@ void checkCompileStatus(GLuint shader) {
static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }";
-class ExampleCustomLayer {
+class ExampleCustomLayer: mbgl::style::CustomLayerHost {
public:
~ExampleCustomLayer() {
- __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "~ExampleCustomLayer");
- if (program) {
- glDeleteBuffers(1, &buffer);
- glDetachShader(program, vertexShader);
- glDetachShader(program, fragmentShader);
- glDeleteShader(vertexShader);
- glDeleteShader(fragmentShader);
- glDeleteProgram(program);
- }
}
void initialize() {
@@ -158,8 +149,15 @@ public:
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW));
}
- void render() {
+ void render(const mbgl::style::CustomLayerRenderParameters&) {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render");
+ glUseProgram(program);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glEnableVertexAttribArray(a_pos);
+ glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL);
+ glDisable(GL_STENCIL_TEST);
+ glDisable(GL_DEPTH_TEST);
+ glUniform4fv(fill_color, 1, color);
GL_CHECK_ERROR(glUseProgram(program));
GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
@@ -172,6 +170,23 @@ public:
}
+ void contextLost() {
+ __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost");
+ program = 0;
+ }
+
+ void deinitialize() {
+ __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize");
+ if (program) {
+ glDeleteBuffers(1, &buffer);
+ glDetachShader(program, vertexShader);
+ glDetachShader(program, fragmentShader);
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteProgram(program);
+ }
+ }
+
GLuint program = 0;
GLuint vertexShader = 0;
GLuint fragmentShader = 0;
@@ -186,7 +201,8 @@ GLfloat ExampleCustomLayer::color[] = { 0.0f, 1.0f, 0.0f, 1.0f };
jlong JNICALL nativeCreateContext(JNIEnv*, jobject) {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeCreateContext");
- return reinterpret_cast<jlong>(new ExampleCustomLayer());
+ auto exampleCustomLayer = std::make_unique<ExampleCustomLayer>();
+ return reinterpret_cast<jlong>(exampleCustomLayer.release());
}
void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) {
@@ -197,26 +213,6 @@ void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat b
ExampleCustomLayer::color[3] = alpha;
}
-void nativeInitialize(void *context) {
- __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeInitialize");
- reinterpret_cast<ExampleCustomLayer*>(context)->initialize();
-}
-
-void nativeRender(void *context, const mbgl::style::CustomLayerRenderParameters& /*parameters*/) {
- __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeRender");
- reinterpret_cast<ExampleCustomLayer*>(context)->render();
-}
-
-void nativeContextLost(void */*context*/) {
- __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeContextLost");
-}
-
-
-void nativeDeinitialize(void *context) {
- __android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeDeinitialize");
- delete reinterpret_cast<ExampleCustomLayer*>(context);
-}
-
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "OnLoad");
@@ -235,22 +231,6 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return JNI_ERR;
}
- env->SetStaticLongField(customLayerClass,
- env->GetStaticFieldID(customLayerClass, "InitializeFunction", "J"),
- reinterpret_cast<jlong>(nativeInitialize));
-
- env->SetStaticLongField(customLayerClass,
- env->GetStaticFieldID(customLayerClass, "RenderFunction", "J"),
- reinterpret_cast<jlong>(nativeRender));
-
- env->SetStaticLongField(customLayerClass,
- env->GetStaticFieldID(customLayerClass, "ContextLostFunction", "J"),
- reinterpret_cast<jlong>(nativeContextLost));
-
- env->SetStaticLongField(customLayerClass,
- env->GetStaticFieldID(customLayerClass, "DeinitializeFunction", "J"),
- reinterpret_cast<jlong>(nativeDeinitialize));
-
return JNI_VERSION_1_6;
}
diff --git a/platform/android/src/file_source.cpp b/platform/android/src/file_source.cpp
index d8d715dbd3..58a91f6cf0 100644
--- a/platform/android/src/file_source.cpp
+++ b/platform/android/src/file_source.cpp
@@ -83,6 +83,13 @@ void FileSource::pause(jni::JNIEnv&) {
}
}
+jni::jboolean FileSource::isResumed(jni::JNIEnv&) {
+ if (activationCounter) {
+ return (jboolean) (activationCounter > 0);
+ }
+ return (jboolean) false;
+}
+
jni::Class<FileSource> FileSource::javaClass;
FileSource* FileSource::getNativePeer(jni::JNIEnv& env, jni::Object<FileSource> jFileSource) {
@@ -114,7 +121,8 @@ void FileSource::registerNative(jni::JNIEnv& env) {
METHOD(&FileSource::setAPIBaseUrl, "setApiBaseUrl"),
METHOD(&FileSource::setResourceTransform, "setResourceTransform"),
METHOD(&FileSource::resume, "activate"),
- METHOD(&FileSource::pause, "deactivate")
+ METHOD(&FileSource::pause, "deactivate"),
+ METHOD(&FileSource::isResumed, "isActivated")
);
}
diff --git a/platform/android/src/file_source.hpp b/platform/android/src/file_source.hpp
index 194f784622..e4295e1b84 100644
--- a/platform/android/src/file_source.hpp
+++ b/platform/android/src/file_source.hpp
@@ -45,6 +45,8 @@ public:
void pause(jni::JNIEnv&);
+ jni::jboolean isResumed(jni::JNIEnv&);
+
static jni::Class<FileSource> javaClass;
static FileSource* getNativePeer(jni::JNIEnv&, jni::Object<FileSource>);
diff --git a/platform/android/src/geojson/conversion/feature.hpp b/platform/android/src/geojson/conversion/feature.hpp
index 86aa5fc03c..8fc62a2789 100644
--- a/platform/android/src/geojson/conversion/feature.hpp
+++ b/platform/android/src/geojson/conversion/feature.hpp
@@ -182,7 +182,7 @@ struct Converter<jni::Object<android::geojson::Feature>, mbgl::Feature> {
auto properties = jni::Object<gson::JsonObject>(*convert<jni::jobject*>(env, value.properties));
// Convert geometry
- auto geometry = jni::Object<android::geojson::Geometry>(*convert<jni::jobject*>(env, value.geometry));
+ auto geometry = *convert<jni::Object<android::geojson::Geometry>>(env, value.geometry);
// Create feature
auto feature = android::geojson::Feature::fromGeometry(env, geometry, properties, jid);
diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp
index 2ca63e2c11..242a68df02 100644
--- a/platform/android/src/geojson/conversion/geometry.hpp
+++ b/platform/android/src/geojson/conversion/geometry.hpp
@@ -1,184 +1,24 @@
#pragma once
-#include "../../conversion/constant.hpp"
-#include "../../conversion/collection.hpp"
-
#include <mapbox/geometry.hpp>
+#include "../geometry.hpp"
#include <jni/jni.hpp>
-#include "../../jni/local_object.hpp"
namespace mbgl {
namespace android {
namespace conversion {
/**
- * Turn mapbox::geometry type into Java GeoJson Geometries
- */
-template <typename T>
-class GeometryEvaluator {
-public:
-
- jni::JNIEnv& env;
-
- /**
- * Point (double[])
- */
- 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;");
-
- // 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()));
- }
-
- /**
- * LineString (double[][])
- */
- 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;");
-
- // 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()));
- }
-
- /**
- * MultiPoint (double[][])
- */
- 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;");
-
- // 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()));
- }
-
- /**
- * Polygon (double[][][])
- */
- 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;");
-
- // 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()));
- }
-
- /**
- * MultiLineString (double[][][])
- */
- jni::jobject* operator()(const mapbox::geometry::multi_line_string<T> &geometry) const {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release();
- static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/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()));
- }
-
- /**
- * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D
- */
- 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));
-
- 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());
- }
-
- // 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()));
- }
-
- /**
- * 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();
- jni::LocalObject<jni::jarray<jni::jobject>> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, collection.size(), *geometryClass));
-
- for(size_t i = 0; i < collection.size(); i = i + 1) {
- auto& geometry = collection.at(i);
- jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mapbox::geometry::geometry<T>::visit(geometry, *this));
- jni::SetObjectArrayElement(env, *jarray, i, converted.get());
- }
-
- // Turn into array list and create the GeometryCollection
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release();
- static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;");
-
- jni::LocalObject<jni::jobject> list = jni::NewLocalObject(env, toArrayList<>(env, *jarray));
- return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, list.get()));
- }
-
-private:
-
- /**
- * x, y -> jarray<jdouble> ([x,y])
- */
- static jni::jarray<jni::jdouble>* 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>> -> 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);
-
- 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());
- }
-
- return &jarray;
- }
-
- /**
- * polygon<T>
- * multi_line_string<T>
- * -> jarray<jobject> (double[][][]) -> [[D + Object array == [[[D
- */
- 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);
-
- 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());
- }
-
- return &jarray;
- }
-};
-
-/**
- * mapbox::geometry::geometry<T> -> Java GeoJson Geometry<>
+ * mapbox::geometry::geometry<T> -> Java GeoJson Geometry
*/
template <class T>
-struct Converter<jni::jobject*, mapbox::geometry::geometry<T>> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const {
- GeometryEvaluator<double> evaluator { env } ;
- jni::jobject* converted = mapbox::geometry::geometry<double>::visit(value, evaluator);
- return {converted};
+struct Converter<jni::Object<android::geojson::Geometry>, mapbox::geometry::geometry<T>> {
+ Result<jni::Object<android::geojson::Geometry>> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const {
+ return { android::geojson::Geometry::New(env, value) };
}
};
-}
-}
-}
+} // conversion
+} // android
+} // mbgl
diff --git a/platform/android/src/geojson/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..18a41d48fa 100644
--- a/platform/android/src/geojson/feature_collection.cpp
+++ b/platform/android/src/geojson/feature_collection.cpp
@@ -7,24 +7,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..5635b5a0f5 100644
--- a/platform/android/src/geojson/geometry.cpp
+++ b/platform/android/src/geojson/geometry.cpp
@@ -6,6 +6,7 @@
#include "multi_line_string.hpp"
#include "polygon.hpp"
#include "multi_polygon.hpp"
+#include "geometry_collection.hpp"
#include <string>
@@ -13,7 +14,49 @@ namespace mbgl {
namespace android {
namespace geojson {
-mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
+/**
+ * Turn mapbox::geometry type into Java GeoJson Geometries
+ */
+class GeometryEvaluator {
+public:
+
+ jni::JNIEnv& env;
+
+ jni::Object<Geometry> operator()(const mbgl::Point<double> &geometry) const {
+ return jni::Cast(env, Point::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::LineString<double> &geometry) const {
+ return jni::Cast(env, LineString::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiLineString<double> &geometry) const {
+ return jni::Cast(env, MultiLineString::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiPoint<double> &geometry) const {
+ return jni::Cast(env, MultiPoint::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::Polygon<double> &geometry) const {
+ return jni::Cast(env, Polygon::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mbgl::MultiPolygon<double> &geometry) const {
+ return jni::Cast(env, MultiPolygon::New(env, geometry), Geometry::javaClass);
+ }
+
+ jni::Object<Geometry> operator()(const mapbox::geometry::geometry_collection<double> &geometry) const {
+ return jni::Cast(env, GeometryCollection::New(env, geometry), Geometry::javaClass);
+ }
+};
+
+jni::Object<Geometry> Geometry::New(jni::JNIEnv& env, mbgl::Geometry<double> geometry) {
+ GeometryEvaluator evaluator { env } ;
+ return mbgl::Geometry<double>::visit(geometry, evaluator);
+}
+
+mbgl::Geometry<double> Geometry::convert(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
auto type = Geometry::getType(env, jGeometry);
if (type == Point::Type()) {
return { Point::convert(env, jni::Object<Point>(jGeometry.Get())) };
@@ -27,13 +70,15 @@ mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geomet
return { Polygon::convert(env, jni::Object<Polygon>(jGeometry.Get())) };
} else if (type == MultiPolygon::Type()) {
return { MultiPolygon::convert(env, jni::Object<MultiPolygon>(jGeometry.Get())) };
+ } else if (type == GeometryCollection::Type()) {
+ return { GeometryCollection::convert(env, jni::Object<GeometryCollection>(jGeometry.Get())) };
}
throw std::runtime_error(std::string {"Unsupported GeoJSON type: " } + type);
}
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..a1bb886683 100644
--- a/platform/android/src/geojson/geometry.hpp
+++ b/platform/android/src/geojson/geometry.hpp
@@ -1,8 +1,10 @@
#pragma once
-#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include "../java/util.hpp"
+
#include <jni/jni.hpp>
namespace mbgl {
@@ -11,9 +13,11 @@ 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 jni::Object<Geometry> New(jni::JNIEnv&, mbgl::Geometry<double>);
- static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object<Geometry>);
+ static mbgl::Geometry<double> convert(jni::JNIEnv&, jni::Object<Geometry>);
static std::string getType(jni::JNIEnv&, jni::Object<Geometry>);
diff --git a/platform/android/src/geojson/geometry_collection.cpp b/platform/android/src/geojson/geometry_collection.cpp
new file mode 100644
index 0000000000..eb3a790404
--- /dev/null
+++ b/platform/android/src/geojson/geometry_collection.cpp
@@ -0,0 +1,63 @@
+#include "geometry_collection.hpp"
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+jni::Object<GeometryCollection> GeometryCollection::New(jni::JNIEnv& env, const mapbox::geometry::geometry_collection<double>& collection) {
+ // Create an array of geometries
+ auto jarray = jni::Array<jni::Object<Geometry>>::New(env, collection.size(), Geometry::javaClass);
+
+ for (size_t i = 0; i < collection.size(); i++) {
+ auto& geometry = collection.at(i);
+ auto jGeometry = Geometry::New(env, geometry);
+ jarray.Set(env, i, jGeometry);
+ jni::DeleteLocalRef(env, jGeometry);
+ }
+
+ // Turn into array list
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+
+ // create the GeometryCollection
+ static auto method = javaClass.GetStaticMethod<jni::Object<GeometryCollection> (jni::Object<java::util::List>)>(env, "fromGeometries");
+ auto jCollection = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jCollection;
+}
+
+mapbox::geometry::geometry_collection<double> GeometryCollection::convert(jni::JNIEnv &env, jni::Object<GeometryCollection> jCollection) {
+ // Get geometries
+ static auto getGeometries = javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getGeometries");
+ auto jList = jCollection.Call(env, getGeometries);
+
+ // Turn into array
+ auto jarray = java::util::List::toArray<Geometry>(env, jList);
+ jni::DeleteLocalRef(env, jList);
+
+ // Convert each geometry
+ mapbox::geometry::geometry_collection<double> collection{};
+
+ auto size = jarray.Length(env);
+ for (jni::jsize i = 0; i < size; i++) {
+ auto element = jarray.Get(env, i);
+ collection.push_back(Geometry::convert(env, element));
+ jni::DeleteLocalRef(env, element);
+ }
+
+ jni::DeleteLocalRef(env, jarray);
+ return collection;
+}
+
+void GeometryCollection::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<GeometryCollection>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<GeometryCollection> GeometryCollection::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/geometry_collection.hpp b/platform/android/src/geojson/geometry_collection.hpp
new file mode 100644
index 0000000000..9ed9953b0d
--- /dev/null
+++ b/platform/android/src/geojson/geometry_collection.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class GeometryCollection : public Geometry {
+public:
+ static constexpr auto Name() { return "com/mapbox/geojson/GeometryCollection"; };
+
+ static constexpr auto Type() { return "GeometryCollection"; };
+
+ static jni::Object<GeometryCollection> New(jni::JNIEnv&, const mapbox::geometry::geometry_collection<double>&);
+
+ static mapbox::geometry::geometry_collection<double> convert(jni::JNIEnv&, jni::Object<GeometryCollection>);
+
+ static jni::Class<GeometryCollection> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp
index d0719f2538..a5f1a468ce 100644
--- a/platform/android/src/geojson/line_string.cpp
+++ b/platform/android/src/geojson/line_string.cpp
@@ -1,44 +1,56 @@
#include "line_string.hpp"
-
-#include "position.hpp"
+#include "point.hpp"
+#include "util.hpp"
+#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<LineString> LineString::New(jni::JNIEnv& env, const mbgl::LineString<double>& lineString) {
+ auto jList = asPointsList(env, lineString);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<LineString>(jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jLineString = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jLineString;
+}
+
mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
mapbox::geojson::line_string lineString;
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..98dc414642 100644
--- a/platform/android/src/geojson/line_string.hpp
+++ b/platform/android/src/geojson/line_string.hpp
@@ -1,28 +1,32 @@
#pragma once
#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
+#include "geometry.hpp"
#include "../java/util.hpp"
+#include <jni/jni.hpp>
+
namespace mbgl {
namespace android {
namespace geojson {
-class LineString : private mbgl::util::noncopyable {
+class LineString : public Geometry {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/LineString"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/LineString"; };
static constexpr auto Type() { return "LineString"; };
+ static jni::Object<LineString> New(jni::JNIEnv&, const mbgl::LineString<double>&);
+
static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<LineString>);
- static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<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..4a6ea37dd1 100644
--- a/platform/android/src/geojson/multi_line_string.cpp
+++ b/platform/android/src/geojson/multi_line_string.cpp
@@ -1,36 +1,47 @@
#include "multi_line_string.hpp"
#include "line_string.hpp"
+#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiLineString> MultiLineString::New(jni::JNIEnv& env, const mbgl::MultiLineString<double>& multiLineString) {
+ auto jList = asPointsListsList(env, multiLineString);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiLineString> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiLineString = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiLineString;
+}
+
mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<MultiLineString> jMultiLineString) {
mapbox::geojson::multi_line_string multiLineString;
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 +50,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..934a0cb6b5 100644
--- a/platform/android/src/geojson/multi_line_string.hpp
+++ b/platform/android/src/geojson/multi_line_string.hpp
@@ -3,25 +3,28 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
#include "../java/util.hpp"
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
-class MultiLineString : private mbgl::util::noncopyable {
+class MultiLineString : public Geometry {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiLineString"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiLineString"; };
static constexpr auto Type() { return "MultiLineString"; };
+ static jni::Object<MultiLineString> New(jni::JNIEnv&, const mbgl::MultiLineString<double>&);
+
static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<MultiLineString>);
- static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<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..6f62541209 100644
--- a/platform/android/src/geojson/multi_point.cpp
+++ b/platform/android/src/geojson/multi_point.cpp
@@ -8,20 +8,30 @@ namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiPoint> MultiPoint::New(JNIEnv& env, const mbgl::MultiPoint<double>& multiPoint) {
+ auto jList = asPointsList(env, multiPoint);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiPoint>(jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiPoint = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiPoint;
+}
+
mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
mapbox::geojson::multi_point multiPoint;
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..cfe80cd34c 100644
--- a/platform/android/src/geojson/multi_point.hpp
+++ b/platform/android/src/geojson/multi_point.hpp
@@ -1,25 +1,29 @@
#pragma once
#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/geometry.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
+#include "geometry.hpp"
#include "../java/util.hpp"
+#include <jni/jni.hpp>
+
namespace mbgl {
namespace android {
namespace geojson {
-class MultiPoint : private mbgl::util::noncopyable {
+class MultiPoint : public Geometry {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPoint"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiPoint"; };
static constexpr auto Type() { return "MultiPoint"; };
+ static jni::Object<MultiPoint> New(jni::JNIEnv&, const mbgl::MultiPoint<double>&);
+
static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object<MultiPoint>);
- static jni::Object<java::util::List> 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..cc872d4955 100644
--- a/platform/android/src/geojson/multi_polygon.cpp
+++ b/platform/android/src/geojson/multi_polygon.cpp
@@ -1,36 +1,59 @@
#include "multi_polygon.hpp"
#include "polygon.hpp"
+#include "util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<MultiPolygon> MultiPolygon::New(JNIEnv& env, const mbgl::MultiPolygon<double>& multiPolygon) {
+ auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, multiPolygon.size(), java::util::List::javaClass);
+
+ for (size_t i = 0; i < multiPolygon.size(); i++) {
+ auto& geometry = multiPolygon.at(i);
+ auto jPolygon = asPointsListsList(env, geometry);
+ jarray.Set(env, i, jPolygon);
+ jni::DeleteLocalRef(env, jPolygon);
+ }
+
+ // Turn into array list
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+
+ // create the MultiPolygon
+ static auto method = javaClass.GetStaticMethod<jni::Object<MultiPolygon> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jMultiPolygon = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jMultiPolygon;
+}
+
mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Object<MultiPolygon> jMultiPolygon) {
mapbox::geojson::multi_polygon multiPolygon;
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..b4657af09d 100644
--- a/platform/android/src/geojson/multi_polygon.hpp
+++ b/platform/android/src/geojson/multi_polygon.hpp
@@ -3,23 +3,26 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
#include "../java/util.hpp"
+#include "geometry.hpp"
+
+#include <jni/jni.hpp>
namespace mbgl {
namespace android {
namespace geojson {
-class MultiPolygon : private mbgl::util::noncopyable {
+class MultiPolygon : public Geometry {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPolygon"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/MultiPolygon"; };
static constexpr auto Type() { return "MultiPolygon"; };
+ static jni::Object<MultiPolygon> New(jni::JNIEnv&, const mbgl::MultiPolygon<double>&);
+
static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object<MultiPolygon>);
- static jni::Object<java::util::List> 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..8a9656ea14 100644
--- a/platform/android/src/geojson/point.cpp
+++ b/platform/android/src/geojson/point.cpp
@@ -1,19 +1,53 @@
#include "point.hpp"
+#include "../java/util.hpp"
+#include "../java_types.hpp"
+#include "../style/value.hpp"
namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<Point> Point::New(jni::JNIEnv& env, const mbgl::Point<double>& point) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Point> (jni::jdouble, jni::jdouble)>(env, "fromLngLat");
+ return javaClass.Call(env, method, point.x, point.y);
+}
+
mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object<Point> jPoint) {
- 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) {
@@ -25,4 +59,4 @@ jni::Class<Point> Point::javaClass;
} // namespace geojson
} // namespace android
-} // namespace mbgl \ No newline at end of file
+} // namespace mbgl
diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp
index 64ac0af9cc..627bd1b649 100644
--- a/platform/android/src/geojson/point.hpp
+++ b/platform/android/src/geojson/point.hpp
@@ -3,7 +3,8 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
-#include "position.hpp"
+#include "../java/util.hpp"
+#include "geometry.hpp"
#include <jni/jni.hpp>
@@ -11,15 +12,19 @@ namespace mbgl {
namespace android {
namespace geojson {
-class Point : private mbgl::util::noncopyable {
+class Point : public Geometry {
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 jni::Object<Point> New(jni::JNIEnv&, const mbgl::Point<double>&);
+
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..96058b63b3 100644
--- a/platform/android/src/geojson/polygon.cpp
+++ b/platform/android/src/geojson/polygon.cpp
@@ -8,11 +8,21 @@ namespace mbgl {
namespace android {
namespace geojson {
+jni::Object<Polygon> Polygon::New(jni::JNIEnv& env, const mbgl::Polygon<double>& polygon) {
+ auto jList = asPointsListsList(env, polygon);
+
+ static auto method = javaClass.GetStaticMethod<jni::Object<Polygon> (jni::Object<java::util::List>)>(env, "fromLngLats");
+ auto jPolygon = javaClass.Call(env, method, jList);
+
+ jni::DeleteLocalRef(env, jList);
+ return jPolygon;
+}
+
mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
mapbox::geojson::polygon polygon;
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 +30,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 +45,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..f3c23b4d7b 100644
--- a/platform/android/src/geojson/polygon.hpp
+++ b/platform/android/src/geojson/polygon.hpp
@@ -3,25 +3,29 @@
#include <mbgl/util/geojson.hpp>
#include <mbgl/util/noncopyable.hpp>
+#include "geometry.hpp"
+#include "../java/util.hpp"
+
#include <jni/jni.hpp>
-#include "../java/util.hpp"
namespace mbgl {
namespace android {
namespace geojson {
-class Polygon : private mbgl::util::noncopyable {
+class Polygon : public Geometry {
public:
- static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Polygon"; };
+ static constexpr auto Name() { return "com/mapbox/geojson/Polygon"; };
static constexpr auto Type() { return "Polygon"; };
+ static jni::Object<Polygon> New(jni::JNIEnv&, const mbgl::Polygon<double>&);
+
static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object<Polygon>);
- static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<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/geojson/util.hpp b/platform/android/src/geojson/util.hpp
index ece8e52433..5e6d90a953 100644
--- a/platform/android/src/geojson/util.hpp
+++ b/platform/android/src/geojson/util.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "point.hpp"
+
#include <type_traits>
namespace mbgl {
@@ -17,6 +19,42 @@ To convertExplicit(From&& src) {
return *reinterpret_cast<std::add_pointer_t<To>>(&src);
}
+/**
+ * Geometry -> List<Point>
+ */
+template <class T>
+static jni::Object<java::util::List> asPointsList(jni::JNIEnv& env, const T& pointsList) {
+ auto jarray = jni::Array<jni::Object<Point>>::New(env, pointsList.size(), Point::javaClass);
+
+ for (jni::jsize i = 0; i < pointsList.size(); i++) {
+ auto jPoint = Point::New(env, pointsList.at(i));
+ jarray.Set(env, i, jPoint);
+ jni::DeleteLocalRef(env, jPoint);
+ }
+
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+ return jList;
+}
+
+/**
+ * Geometry -> List<List<Point>>
+ */
+template <class SHAPE>
+static jni::Object<java::util::List> asPointsListsList(JNIEnv& env, SHAPE value) {
+ auto jarray = jni::Array<jni::Object<java::util::List>>::New(env, value.size(), java::util::List::javaClass);
+
+ for (size_t i = 0; i < value.size(); i = i + 1) {
+ auto pointsList = asPointsList(env, value[i]);
+ jarray.Set(env, i, pointsList);
+ jni::DeleteLocalRef(env, pointsList);
+ }
+
+ auto jList = java::util::Arrays::asList(env, jarray);
+ jni::DeleteLocalRef(env, jarray);
+ return jList;
+}
+
} // namespace geojson
} // namespace android
} // namespace mbgl
diff --git a/platform/android/src/geometry/lat_lng_bounds.cpp b/platform/android/src/geometry/lat_lng_bounds.cpp
index ec1a32fed5..827ee52e95 100644
--- a/platform/android/src/geometry/lat_lng_bounds.cpp
+++ b/platform/android/src/geometry/lat_lng_bounds.cpp
@@ -9,14 +9,17 @@ jni::Object<LatLngBounds> LatLngBounds::New(jni::JNIEnv& env, mbgl::LatLngBounds
}
mbgl::LatLngBounds LatLngBounds::getLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> bounds) {
- static auto swLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeSouth");
- static auto swLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeWest");
- static auto neLat = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeNorth");
- static auto neLon = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeEast");
- return mbgl::LatLngBounds::hull(
- { bounds.Get(env, swLat), bounds.Get(env, swLon) },
- { bounds.Get(env, neLat), bounds.Get(env, neLon) }
- );
+ static auto swLatField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeSouth");
+ static auto swLonField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeWest");
+ static auto neLatField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "latitudeNorth");
+ static auto neLonField = LatLngBounds::javaClass.GetField<jni::jdouble>(env, "longitudeEast");
+
+ mbgl::LatLng sw = { bounds.Get(env, swLatField), bounds.Get(env, swLonField) };
+ mbgl::LatLng ne = { bounds.Get(env, neLatField), bounds.Get(env, neLonField) };
+
+ sw.unwrapForShortestPath(ne);
+
+ return mbgl::LatLngBounds::hull(sw, ne);
}
void LatLngBounds::registerNative(jni::JNIEnv& env) {
diff --git a/platform/android/src/gson/json_array.cpp b/platform/android/src/gson/json_array.cpp
index d91e323ac9..e8852d77e9 100644
--- a/platform/android/src/gson/json_array.cpp
+++ b/platform/android/src/gson/json_array.cpp
@@ -6,7 +6,22 @@ namespace mbgl {
namespace android {
namespace gson {
-std::vector<mapbox::geometry::value> JsonArray::convert(jni::JNIEnv &env, jni::Object<JsonArray> jsonArray) {
+jni::Object<JsonArray> JsonArray::New(jni::JNIEnv& env, const std::vector<mapbox::geometry::value>& values){
+ static auto constructor = JsonArray::javaClass.GetConstructor(env);
+ static auto addMethod = JsonArray::javaClass.GetMethod<void (jni::Object<JsonElement>)>(env, "add");
+
+ auto jsonArray = JsonArray::javaClass.New(env, constructor);
+
+ for (const auto &v : values) {
+ auto jsonElement = JsonElement::New(env, v);
+ jsonArray.Call(env, addMethod, jsonElement);
+ jni::DeleteLocalRef(env, jsonElement);
+ }
+
+ return jsonArray;
+}
+
+std::vector<mapbox::geometry::value> JsonArray::convert(jni::JNIEnv& env, const jni::Object<JsonArray> jsonArray) {
std::vector<mapbox::geometry::value> values;
if (jsonArray) {
@@ -28,7 +43,7 @@ std::vector<mapbox::geometry::value> JsonArray::convert(jni::JNIEnv &env, jni::O
return values;
}
-void JsonArray::registerNative(jni::JNIEnv &env) {
+void JsonArray::registerNative(jni::JNIEnv& env) {
// Lookup the class
javaClass = *jni::Class<JsonArray>::Find(env).NewGlobalRef(env).release();
}
diff --git a/platform/android/src/gson/json_array.hpp b/platform/android/src/gson/json_array.hpp
index 8571ad5dba..c9ae98692f 100644
--- a/platform/android/src/gson/json_array.hpp
+++ b/platform/android/src/gson/json_array.hpp
@@ -13,6 +13,8 @@ class JsonArray : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/google/gson/JsonArray"; };
+ static jni::Object<JsonArray> New(jni::JNIEnv&, const std::vector<mapbox::geometry::value>&);
+
static std::vector<mapbox::geometry::value> convert(JNIEnv&, jni::Object<JsonArray>);
static jni::Class<JsonArray> javaClass;
diff --git a/platform/android/src/gson/json_element.cpp b/platform/android/src/gson/json_element.cpp
index 060b1e0fe2..5eaaf531f4 100644
--- a/platform/android/src/gson/json_element.cpp
+++ b/platform/android/src/gson/json_element.cpp
@@ -11,6 +11,34 @@ namespace mbgl {
namespace android {
namespace gson {
+/**
+ * Turn mapbox::geometry::value into Java Gson JsonElement
+ */
+class JsonElementEvaluator {
+public:
+
+ jni::JNIEnv& env;
+
+ jni::Object<JsonElement> operator()(const JsonPrimitive::value value) const {
+ return jni::Cast(env, JsonPrimitive::New(env, value), JsonElement::javaClass);
+ }
+
+ jni::Object<JsonElement> operator()(const std::vector<mapbox::geometry::value> &values) const {
+ return jni::Cast(env, JsonArray::New(env, values), JsonElement::javaClass);
+ }
+
+ jni::Object<JsonElement> operator()(const std::unordered_map<std::string, mapbox::geometry::value> &values) const {
+ return jni::Cast(env, JsonObject::New(env, values), JsonElement::javaClass);
+ }
+
+};
+
+
+jni::Object<JsonElement> JsonElement::New(jni::JNIEnv& env, const mapbox::geometry::value& value) {
+ JsonElementEvaluator evaluator { env } ;
+ return mapbox::geometry::value::visit(value, evaluator);
+}
+
mapbox::geometry::value JsonElement::convert(jni::JNIEnv &env, jni::Object<JsonElement> jsonElement) {
mapbox::geometry::value value;
diff --git a/platform/android/src/gson/json_element.hpp b/platform/android/src/gson/json_element.hpp
index 7619350617..d850caa526 100644
--- a/platform/android/src/gson/json_element.hpp
+++ b/platform/android/src/gson/json_element.hpp
@@ -13,6 +13,8 @@ class JsonElement : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/google/gson/JsonElement"; };
+ static jni::Object<JsonElement> New(jni::JNIEnv&, const mapbox::geometry::value&);
+
static mapbox::geometry::value convert(JNIEnv&, jni::Object<JsonElement>);
static bool isJsonObject(JNIEnv&, jni::Object<JsonElement>);
diff --git a/platform/android/src/gson/json_object.cpp b/platform/android/src/gson/json_object.cpp
index a704dae9dd..61b55f8b9e 100644
--- a/platform/android/src/gson/json_object.cpp
+++ b/platform/android/src/gson/json_object.cpp
@@ -9,6 +9,23 @@ namespace android {
namespace gson {
+jni::Object<JsonObject> JsonObject::New(jni::JNIEnv& env, const std::unordered_map<std::string, mapbox::geometry::value>& values) {
+ static auto constructor = JsonObject::javaClass.GetConstructor(env);
+ static auto addMethod = JsonObject::javaClass.GetMethod<void (jni::String, jni::Object<JsonElement>)>(env, "add");
+
+ jni::Object<JsonObject> jsonObject = JsonObject::javaClass.New(env, constructor);
+
+ for (auto &item : values) {
+ jni::Object<JsonElement> jsonElement = JsonElement::New(env, item.second);
+ jni::String key = jni::Make<jni::String>(env, item.first);
+ jsonObject.Call(env, addMethod, key, jsonElement);
+ jni::DeleteLocalRef(env, jsonElement);
+ jni::DeleteLocalRef(env, key);
+ }
+
+ return jsonObject;
+}
+
template <typename F> // void (jni::String, jni::Object<gson::JsonElement>)
static void iterateEntrySet(jni::JNIEnv& env, jni::Object<JsonObject> jsonObject, F callback) {
// Get Set<Map.Entry<String, JsonElement>>
diff --git a/platform/android/src/gson/json_object.hpp b/platform/android/src/gson/json_object.hpp
index aba8e40415..4bc61e51a2 100644
--- a/platform/android/src/gson/json_object.hpp
+++ b/platform/android/src/gson/json_object.hpp
@@ -13,6 +13,8 @@ class JsonObject : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "com/google/gson/JsonObject"; };
+ static jni::Object<JsonObject> New(jni::JNIEnv&, const std::unordered_map<std::string, mapbox::geometry::value>&);
+
static mapbox::geometry::property_map convert(JNIEnv&, jni::Object<JsonObject>);
static jni::Class<JsonObject> javaClass;
diff --git a/platform/android/src/gson/json_primitive.cpp b/platform/android/src/gson/json_primitive.cpp
index 58d0b45fe7..4e171c4845 100644
--- a/platform/android/src/gson/json_primitive.cpp
+++ b/platform/android/src/gson/json_primitive.cpp
@@ -1,9 +1,89 @@
#include "json_primitive.hpp"
+#include "../java/lang.hpp"
namespace mbgl {
namespace android {
namespace gson {
+/**
+ * Turn mapbox::geometry::value into Java Gson JsonPrimitives
+ */
+class JsonPrimitiveEvaluator {
+public:
+
+ jni::JNIEnv& env;
+
+ /**
+ * Create a null primitive
+ */
+ jni::Object<JsonPrimitive> operator()(const mapbox::geometry::null_value_t) const {
+ return jni::Object<JsonPrimitive>();
+ }
+
+ /**
+ * Create a primitive containing a string value
+ */
+ jni::Object<JsonPrimitive> operator()(const std::string value) const {
+ static auto constructor = JsonPrimitive::javaClass.GetConstructor<jni::String>(env);
+ auto jvalue = jni::Make<jni::String>(env, value);
+ auto jsonPrimitive = JsonPrimitive::javaClass.New(env, constructor, jvalue);
+ jni::DeleteLocalRef(env, jvalue);
+ return jsonPrimitive;
+ }
+
+ /**
+ * Create a primitive containing a number value with type double
+ */
+ jni::Object<JsonPrimitive> operator()(const double value) const {
+ static auto constructor = JsonPrimitive::javaClass.GetConstructor<jni::Object<java::lang::Number>>(env);
+ auto boxedValue = java::lang::Double::valueOf(env, value);
+ auto number = jni::Cast(env, boxedValue, java::lang::Number::javaClass);
+ auto jsonPrimitive = JsonPrimitive::javaClass.New(env, constructor, number);
+ jni::DeleteLocalRef(env, boxedValue);
+ return jsonPrimitive;
+ }
+
+ /**
+ * Create a primitive containing a number value with type long
+ */
+ jni::Object<JsonPrimitive> operator()(const int64_t value) const {
+ static auto constructor = JsonPrimitive::javaClass.GetConstructor<jni::Object<java::lang::Number>>(env);
+ auto boxedValue = java::lang::Long::valueOf(env, value);
+ auto number = jni::Cast(env, boxedValue, java::lang::Number::javaClass);
+ auto jsonPrimitive = JsonPrimitive::javaClass.New(env, constructor, number);
+ jni::DeleteLocalRef(env, boxedValue);
+ return jsonPrimitive;
+ }
+
+ /**
+ * Create a primitive containing a number value with type long
+ */
+ jni::Object<JsonPrimitive> operator()(const uint64_t value) const {
+ static auto constructor = JsonPrimitive::javaClass.GetConstructor<jni::Object<java::lang::Number>>(env);
+ auto boxedValue = java::lang::Long::valueOf(env, value);
+ auto number = jni::Cast(env, boxedValue, java::lang::Number::javaClass);
+ auto jsonPrimitive = JsonPrimitive::javaClass.New(env, constructor, number);
+ jni::DeleteLocalRef(env, boxedValue);
+ return jsonPrimitive;
+ }
+
+ /**
+ * Create a primitive containing a boolean value
+ */
+ jni::Object<JsonPrimitive> operator()(const bool value) const {
+ static auto constructor = JsonPrimitive::javaClass.GetConstructor<jni::Object<java::lang::Boolean>>(env);
+ auto boxedValue = java::lang::Boolean::valueOf(env, value);
+ auto jsonPrimitive = JsonPrimitive::javaClass.New(env, constructor, boxedValue);
+ jni::DeleteLocalRef(env, boxedValue);
+ return jsonPrimitive;
+ }
+};
+
+jni::Object<JsonPrimitive> JsonPrimitive::New(jni::JNIEnv &env, const value& value) {
+ JsonPrimitiveEvaluator evaluator { env };
+ return value::visit(value, evaluator);
+}
+
JsonPrimitive::value JsonPrimitive::convert(jni::JNIEnv &env, jni::Object<JsonPrimitive> jsonPrimitive) {
value value;
if (jsonPrimitive) {
diff --git a/platform/android/src/gson/json_primitive.hpp b/platform/android/src/gson/json_primitive.hpp
index 5fc8a2b485..c418e0ebe8 100644
--- a/platform/android/src/gson/json_primitive.hpp
+++ b/platform/android/src/gson/json_primitive.hpp
@@ -15,6 +15,8 @@ public:
static constexpr auto Name() { return "com/google/gson/JsonPrimitive"; };
+ static jni::Object<JsonPrimitive> New(jni::JNIEnv&, const value&);
+
static value convert(JNIEnv&, jni::Object<JsonPrimitive>);
static bool isBoolean(JNIEnv&, jni::Object<JsonPrimitive>);
diff --git a/platform/android/src/java/lang.cpp b/platform/android/src/java/lang.cpp
new file mode 100644
index 0000000000..3c95737169
--- /dev/null
+++ b/platform/android/src/java/lang.cpp
@@ -0,0 +1,76 @@
+#include "lang.hpp"
+
+namespace mbgl {
+namespace android {
+namespace java {
+namespace lang {
+
+// Float
+
+jni::Object<Float> Float::valueOf(JNIEnv &env, jfloat value) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Float> (jni::jfloat)>(env, "valueOf");
+ return javaClass.Call(env, method, value);
+}
+
+void Float::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Float>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Float> Float::javaClass;
+
+// Long
+
+jni::Object<Long> Long::valueOf(JNIEnv &env, jlong value) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Long> (jni::jlong)>(env, "valueOf");
+ return javaClass.Call(env, method, value);
+}
+
+void Long::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Long>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Long> Long::javaClass;
+
+// Double
+
+jni::Object<Double> Double::valueOf(JNIEnv &env, jdouble value) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Double> (jni::jdouble)>(env, "valueOf");
+ return javaClass.Call(env, method, value);
+}
+
+void Double::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Double>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Double> Double::javaClass;
+
+// Boolean
+
+jni::Object<Boolean> Boolean::valueOf(JNIEnv &env, jboolean value) {
+ static auto method = javaClass.GetStaticMethod<jni::Object<Boolean> (jni::jboolean)>(env, "valueOf");
+ return javaClass.Call(env, method, value);
+}
+
+void Boolean::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Boolean>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Boolean> Boolean::javaClass;
+
+// Number
+
+void Number::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Number>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Number> Number::javaClass;
+
+} // namespace lang
+} // namespace java
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/java/lang.hpp b/platform/android/src/java/lang.hpp
index dcf81a9d0c..981e3b14b7 100644
--- a/platform/android/src/java/lang.hpp
+++ b/platform/android/src/java/lang.hpp
@@ -1,18 +1,64 @@
#pragma once
+#include <jni/jni.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
namespace mbgl {
namespace android {
namespace java {
namespace lang {
-class Float {
+class Float : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/lang/Float"; };
+
+ static jni::Object<Float> valueOf(JNIEnv&, jfloat);
+
+ static jni::Class<Float> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+class Double : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "java/lang/Double"; };
+
+ static jni::Object<Double> valueOf(JNIEnv&, jdouble);
+
+ static jni::Class<Double> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+class Long : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "java/lang/Long"; };
+
+ static jni::Object<Long> valueOf(JNIEnv&, jlong);
+
+ static jni::Class<Long> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+class Boolean : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "java/lang/Boolean"; };
+
+ static jni::Object<Boolean> valueOf(JNIEnv&, jboolean);
+
+ static jni::Class<Boolean> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
};
-class Number {
+class Number : private mbgl::util::noncopyable {
public:
static constexpr auto Name() { return "java/lang/Number"; };
+
+ static jni::Class<Number> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
};
} // namespace lang
diff --git a/platform/android/src/java/util.cpp b/platform/android/src/java/util.cpp
index effd2ae0d0..89c4c77794 100644
--- a/platform/android/src/java/util.cpp
+++ b/platform/android/src/java/util.cpp
@@ -5,12 +5,14 @@ namespace android {
namespace java {
namespace util {
+jni::Class<Arrays> Arrays::javaClass;
jni::Class<List> List::javaClass;
jni::Class<Set> Set::javaClass;
jni::Class<Map> Map::javaClass;
jni::Class<Map::Entry> Map::Entry::javaClass;
void registerNative(jni::JNIEnv& env) {
+ Arrays::javaClass = *jni::Class<Arrays>::Find(env).NewGlobalRef(env).release();
List::javaClass = *jni::Class<List>::Find(env).NewGlobalRef(env).release();
Set::javaClass = *jni::Class<Set>::Find(env).NewGlobalRef(env).release();
Map::javaClass = *jni::Class<Map>::Find(env).NewGlobalRef(env).release();
diff --git a/platform/android/src/java/util.hpp b/platform/android/src/java/util.hpp
index dedb8ac348..c6b07acac5 100644
--- a/platform/android/src/java/util.hpp
+++ b/platform/android/src/java/util.hpp
@@ -24,6 +24,21 @@ public:
};
+class Arrays : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "java/util/Arrays"; };
+
+ template <class T>
+ static jni::Object<List> asList(jni::JNIEnv& env, jni::Array<jni::Object<T>> array) {
+ static auto asList = Arrays::javaClass.GetStaticMethod<jni::Object<List>(jni::Array<jni::Object<>>)>(env, "asList");
+ return javaClass.Call(env, asList, (jni::Array<jni::Object<>>) array);
+ }
+
+ static jni::Class<Arrays> javaClass;
+
+};
+
class Set : private mbgl::util::noncopyable {
public:
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index e75a9c6985..2f6ed96ab0 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -14,13 +14,13 @@
#include "geojson/feature.hpp"
#include "geojson/feature_collection.hpp"
#include "geojson/geometry.hpp"
+#include "geojson/geometry_collection.hpp"
#include "geojson/line_string.hpp"
#include "geojson/multi_line_string.hpp"
#include "geojson/multi_point.hpp"
#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"
@@ -41,17 +41,13 @@
#include "offline/offline_region_error.hpp"
#include "offline/offline_region_status.hpp"
#include "style/transition_options.hpp"
-#include "style/functions/categorical_stops.hpp"
-#include "style/functions/exponential_stops.hpp"
-#include "style/functions/identity_stops.hpp"
-#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"
#include "text/local_glyph_rasterizer_jni.hpp"
+#include "java/lang.hpp"
namespace mbgl {
namespace android {
@@ -117,18 +113,23 @@ void registerNatives(JavaVM *vm) {
java::util::registerNative(env);
PointF::registerNative(env);
RectF::registerNative(env);
+ java::lang::Number::registerNative(env);
+ java::lang::Float::registerNative(env);
+ java::lang::Boolean::registerNative(env);
+ java::lang::Double::registerNative(env);
+ java::lang::Long::registerNative(env);
// GeoJSON
geojson::Feature::registerNative(env);
geojson::FeatureCollection::registerNative(env);
geojson::Geometry::registerNative(env);
+ geojson::GeometryCollection::registerNative(env);
geojson::LineString::registerNative(env);
geojson::MultiLineString::registerNative(env);
geojson::MultiPoint::registerNative(env);
geojson::MultiPolygon::registerNative(env);
geojson::Point::registerNative(env);
geojson::Polygon::registerNative(env);
- geojson::Position::registerNative(env);
// Geometry
LatLng::registerNative(env);
@@ -162,14 +163,9 @@ void registerNatives(JavaVM *vm) {
// Style
TransitionOptions::registerNative(env);
registerNativeLayers(env);
- registerNativeSources(env);
+ Source::registerNative(env);
Light::registerNative(env);
Position::registerNative(env);
- Stop::registerNative(env);
- CategoricalStops::registerNative(env);
- ExponentialStops::registerNative(env);
- IdentityStops::registerNative(env);
- IntervalStops::registerNative(env);
// Map
CameraPosition::registerNative(env);
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 04cbb21927..eecb76213b 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -288,13 +288,15 @@ void NativeMapView::setLatLng(jni::JNIEnv&, jni::jdouble latitude, jni::jdouble
map->setLatLng(mbgl::LatLng(latitude, longitude), insets, mbgl::AnimationOptions{mbgl::Milliseconds(duration)});
}
-jni::Object<CameraPosition> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> jBounds) {
- return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), insets));
+jni::Object<CameraPosition> NativeMapView::getCameraForLatLngBounds(jni::JNIEnv& env, jni::Object<LatLngBounds> jBounds, double top, double left, double bottom, double right) {
+ mbgl::EdgeInsets padding = {top, left, bottom, right};
+ return CameraPosition::New(env, map->cameraForLatLngBounds(mbgl::android::LatLngBounds::getLatLngBounds(env, jBounds), padding));
}
-jni::Object<CameraPosition> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, jni::Object<geojson::Geometry> jGeometry, double bearing) {
+jni::Object<CameraPosition> NativeMapView::getCameraForGeometry(jni::JNIEnv& env, jni::Object<geojson::Geometry> jGeometry, double bearing, double top, double left, double bottom, double right) {
auto geometry = geojson::Geometry::convert(env, jGeometry);
- return CameraPosition::New(env, map->cameraForGeometry(geometry, insets, bearing));
+ mbgl::EdgeInsets padding = {top, left, bottom, right};
+ return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing));
}
void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) {
@@ -450,7 +452,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 +858,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,50 +874,30 @@ 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) {
- jni::NullCheck(env, &pixels);
- std::size_t size = pixels.Length(env);
-
- mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(w), static_cast<uint32_t>(h) });
- if (premultipliedImage.bytes() != uint32_t(size)) {
- throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
- }
-
- jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
+void NativeMapView::addImage(JNIEnv& env, jni::String name, jni::Object<Bitmap> bitmap, jni::jfloat scale) {
+ jni::NullCheck(env, &bitmap);
+ mbgl::PremultipliedImage premultipliedImage = Bitmap::GetImage(env, bitmap);
map->getStyle().addImage(std::make_unique<mbgl::style::Image>(
jni::Make<std::string>(env, name),
@@ -1048,7 +1028,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 1b6371d726..aff3b51c42 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"
@@ -104,9 +104,9 @@ public:
void setLatLng(jni::JNIEnv&, jni::jdouble, jni::jdouble, jni::jlong);
- jni::Object<CameraPosition> getCameraForLatLngBounds(jni::JNIEnv&, jni::Object<mbgl::android::LatLngBounds>);
+ jni::Object<CameraPosition> getCameraForLatLngBounds(jni::JNIEnv&, jni::Object<mbgl::android::LatLngBounds>, double top, double left, double bottom, double right);
- jni::Object<CameraPosition> getCameraForGeometry(jni::JNIEnv&, jni::Object<geojson::Geometry>, double bearing);
+ jni::Object<CameraPosition> getCameraForGeometry(jni::JNIEnv&, jni::Object<geojson::Geometry>, double bearing, double top, double left, double bottom, double right);
void setReachability(jni::JNIEnv&, jni::jboolean);
@@ -230,13 +230,13 @@ 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>);
+ void addImage(JNIEnv&, jni::String, jni::Object<Bitmap> bitmap, jni::jfloat);
void addImages(JNIEnv&, jni::Array<jni::Object<mbgl::android::Image>>);
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/function.hpp b/platform/android/src/style/conversion/function.hpp
index ad01a7afc2..d6669b4508 100644
--- a/platform/android/src/style/conversion/function.hpp
+++ b/platform/android/src/style/conversion/function.hpp
@@ -5,13 +5,9 @@
#include "../../conversion/constant.hpp"
#include "types.hpp"
#include "../../java/lang.hpp"
-#include "../functions/stop.hpp"
-#include "../functions/categorical_stops.hpp"
-#include "../functions/exponential_stops.hpp"
-#include "../functions/identity_stops.hpp"
-#include "../functions/interval_stops.hpp"
#include <jni/jni.hpp>
+#include "../../gson/json_element.hpp"
#include <tuple>
#include <map>
@@ -20,205 +16,33 @@ namespace mbgl {
namespace android {
namespace conversion {
-/**
- * Conversion from core composite value to java type
- */
-class CategoricalValueEvaluator {
-public:
-
- CategoricalValueEvaluator(jni::JNIEnv& _env) : env(_env) {}
-
- template <class T>
- jni::jobject* operator()(const T &value) const {
- return *convert<jni::jobject*, T>(env, value);
- }
-
-private:
- jni::JNIEnv& env;
-};
-
-/**
- * Conversion from core composite value to java type
- */
-template <>
-struct Converter<jni::jobject*, mbgl::style::CategoricalValue> {
-
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CategoricalValue& value) const {
- CategoricalValueEvaluator evaluator(env);
- return apply_visitor(evaluator, value);
- }
-};
-
-template <class I, class O>
-jni::Array<jni::Object<Stop>> toFunctionStopJavaArray(jni::JNIEnv& env, std::map<I, O> value) {
-
- auto jarray = jni::Array<jni::Object<Stop>>::New(env, value.size(), Stop::javaClass);
-
- size_t i = 0;
- for (auto const& stop : value) {
- jni::jobject* in = *convert<jni::jobject*, I>(env, stop.first);
- jni::jobject* out = *convert<jni::jobject*, O>(env, stop.second);
-
- auto jstop = Stop::New(env, jni::Object<>(in), jni::Object<>(out));
- jarray.Set(env, i, jstop);
-
- jni::DeleteLocalRef(env, in);
- jni::DeleteLocalRef(env, out);
- jni::DeleteLocalRef(env, jstop);
-
- i++;
- }
-
- return jarray;
-}
-
-template <class I, class O>
-jni::Array<jni::Object<Stop>> toFunctionStopJavaArray(jni::JNIEnv& env, std::map<float, std::map<I, O>> value) {
-
- auto jarray = jni::Array<jni::Object<Stop>>::New(env, value.size(), Stop::javaClass);
-
- for (auto const& zoomLevelMap : value) {
- size_t i = 0;
- for (auto const& stop: zoomLevelMap.second) {
- auto zoom = jni::Object<java::lang::Number>(*convert<jni::jobject*>(env, zoomLevelMap.first));
- auto in = jni::Object<>(*convert<jni::jobject*, I>(env, stop.first));
- auto out = jni::Object<>(*convert<jni::jobject*, O>(env, stop.second));
- auto compositeValue = Stop::CompositeValue::New(env, zoom, in);
-
- auto jstop = Stop::New(env, compositeValue, out);
- jarray.Set(env, i, jstop);
-
- jni::DeleteLocalRef(env, zoom);
- jni::DeleteLocalRef(env, in);
- jni::DeleteLocalRef(env, out);
- jni::DeleteLocalRef(env, compositeValue);
- jni::DeleteLocalRef(env, jstop);
-
- i++;
- }
- }
-
- return jarray;
-}
-
-template <class I, typename O>
-inline jni::jobject* convertCompositeStopsArray(jni::JNIEnv& env, std::map<float, std::map<I, O>> value) {
- static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/stops/Stop")).release();
- static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Object;)V");
-
- jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *javaClass);
-
- size_t i = 0;
- for (auto const& stop : value) {
- jni::jobject* in = *convert<jni::jobject*, I>(env, stop.first);
- jni::jobject* out = *convert<jni::jobject*, O>(env, stop.second);
- jni::SetObjectArrayElement(env, jarray, i, &jni::NewObject(env, *javaClass, *constructor, in, out));
- i++;
- jni::DeleteLocalRef(env, in);
- jni::DeleteLocalRef(env, out);
- }
-
- return &jarray;
-}
-
-/**
- * Conversion from core function stops to Stops java subclasses
- */
template <class T>
-class StopsEvaluator {
-public:
-
- StopsEvaluator(jni::JNIEnv& _env) : env(_env) {}
-
- jni::jobject* operator()(const mbgl::style::CategoricalStops<T> &value) const {
- return CategoricalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get();
- }
-
- jni::jobject* operator()(const mbgl::style::CompositeCategoricalStops<T> &value) const {
- return CategoricalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get();
- }
-
- jni::jobject* operator()(const mbgl::style::ExponentialStops<T> &value) const {
- return ExponentialStops::New(env, jni::Object<java::lang::Float>(*convert<jni::jobject*>(env, value.base)), toFunctionStopJavaArray(env, value.stops)).Get();
- }
+struct Converter<jni::Object<android::gson::JsonElement>, mbgl::style::CameraFunction<T>> {
- jni::jobject* operator()(const mbgl::style::CompositeExponentialStops<T> &value) const {
- return ExponentialStops::New(env, jni::Object<java::lang::Float>(*convert<jni::jobject*>(env, value.base)), toFunctionStopJavaArray(env, value.stops)).Get();
+ Result<jni::Object<android::gson::JsonElement>> operator()(jni::JNIEnv& env, const mbgl::style::CameraFunction<T>& value) const {
+ // Convert expressions
+ mbgl::Value expressionValue = value.getExpression().serialize();
+ return gson::JsonElement::New(env, expressionValue);
}
-
- jni::jobject* operator()(const mbgl::style::IdentityStops<T> &) const {
- return IdentityStops::New(env).Get();
- }
-
- jni::jobject* operator()(const mbgl::style::IntervalStops<T> &value) const {
- return IntervalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get();
- }
-
- jni::jobject* operator()(const mbgl::style::CompositeIntervalStops<T> &value) const {
- return IntervalStops::New(env, toFunctionStopJavaArray(env, value.stops)).Get();
- }
-
-private:
- jni::JNIEnv& env;
};
template <class T>
-struct Converter<jni::jobject*, mbgl::style::CameraFunction<T>> {
+struct Converter<jni::Object<android::gson::JsonElement>, mbgl::style::SourceFunction<T>> {
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CameraFunction<T>& value) const {
- static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/CameraFunction")).release();
- static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>", "(Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V");
-
- StopsEvaluator<T> evaluator(env);
- jni::jobject* stops = apply_visitor(evaluator, value.stops);
- jni::jobject* converted = &jni::NewObject(env, *clazz, *constructor, stops);
-
- return { converted };
- }
-};
-
-template <class T>
-struct Converter<jni::jobject*, mbgl::style::SourceFunction<T>> {
-
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::SourceFunction<T>& value) const {
- static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/SourceFunction")).release();
- static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>",
- "(Ljava/lang/Object;Ljava/lang/String;Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V");
-
- // Convert stops
- StopsEvaluator<T> evaluator(env);
- jni::jobject* stops = apply_visitor(evaluator, value.stops);
-
- // Convert default value
- jni::jobject* defaultValue = nullptr;
- if (value.defaultValue) {
- defaultValue = *convert<jni::jobject*>(env, *value.defaultValue);
- }
-
- return { &jni::NewObject(env, *clazz, *constructor, defaultValue, jni::Make<jni::String>(env, value.property).Get(), stops) };
+ Result<jni::Object<android::gson::JsonElement>> operator()(jni::JNIEnv& env, const mbgl::style::SourceFunction<T>& value) const {
+ // Convert expressions
+ mbgl::Value expressionValue = value.getExpression().serialize();
+ return gson::JsonElement::New(env, expressionValue);
}
};
template <class T>
-struct Converter<jni::jobject*, mbgl::style::CompositeFunction<T>> {
-
- Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::CompositeFunction<T>& value) const {
- static jni::jclass* clazz = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/mapboxsdk/style/functions/CompositeFunction")).release();
- static jni::jmethodID* constructor = &jni::GetMethodID(env, *clazz, "<init>",
- "(Ljava/lang/Object;Ljava/lang/String;Lcom/mapbox/mapboxsdk/style/functions/stops/Stops;)V");
-
- // Convert stops
- StopsEvaluator<T> evaluator(env);
- jni::jobject* stops = apply_visitor(evaluator, value.stops);
-
-
- // Convert default value
- jni::jobject* defaultValue = nullptr;
- if (value.defaultValue) {
- defaultValue = *convert<jni::jobject*>(env, *value.defaultValue);
- }
+struct Converter<jni::Object<android::gson::JsonElement>, mbgl::style::CompositeFunction<T>> {
- return { &jni::NewObject(env, *clazz, *constructor, defaultValue, jni::Make<jni::String>(env, value.property).Get(), stops) };
+ Result<jni::Object<android::gson::JsonElement>> operator()(jni::JNIEnv& env, const mbgl::style::CompositeFunction<T>& value) const {
+ // Convert expressions
+ mbgl::Value expressionValue = value.getExpression().serialize();
+ return gson::JsonElement::New(env, expressionValue);
}
};
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/property_value.hpp b/platform/android/src/style/conversion/property_value.hpp
index a58cf975a7..902d1e80b2 100644
--- a/platform/android/src/style/conversion/property_value.hpp
+++ b/platform/android/src/style/conversion/property_value.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/data_driven_property_value.hpp>
+#include <mbgl/style/heatmap_color_property_value.hpp>
#include "../../conversion/conversion.hpp"
#include "../../conversion/constant.hpp"
#include "types.hpp"
@@ -30,15 +31,15 @@ public:
}
jni::jobject* operator()(const mbgl::style::CameraFunction<T> &value) const {
- return *convert<jni::jobject*, mbgl::style::CameraFunction<T>>(env, value);
+ return *convert<jni::Object<android::gson::JsonElement>, mbgl::style::CameraFunction<T>>(env, value);
}
jni::jobject* operator()(const mbgl::style::SourceFunction<T> &value) const {
- return *convert<jni::jobject*, mbgl::style::SourceFunction<T>>(env, value);
+ return *convert<jni::Object<android::gson::JsonElement>, mbgl::style::SourceFunction<T>>(env, value);
}
jni::jobject* operator()(const mbgl::style::CompositeFunction<T> &value) const {
- return *convert<jni::jobject*, mbgl::style::CompositeFunction<T>>(env, value);
+ return *convert<jni::Object<android::gson::JsonElement>, mbgl::style::CompositeFunction<T>>(env, value);
}
private:
@@ -70,6 +71,18 @@ struct Converter<jni::jobject*, mbgl::style::DataDrivenPropertyValue<T>> {
}
};
+/**
+ * Convert core heat map color property value to java
+ */
+template <>
+struct Converter<jni::jobject*, mbgl::style::HeatmapColorPropertyValue> {
+
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::style::HeatmapColorPropertyValue value) const {
+ PropertyValueEvaluator<mbgl::style::HeatmapColorPropertyValue> evaluator(env);
+ return *convert<jni::jobject*>(env, value.evaluate(evaluator));
+ }
+};
+
} // namespace conversion
} // namespace android
} // 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/functions/categorical_stops.cpp b/platform/android/src/style/functions/categorical_stops.cpp
deleted file mode 100644
index 2aff9730a7..0000000000
--- a/platform/android/src/style/functions/categorical_stops.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "categorical_stops.hpp"
-
-namespace mbgl {
-namespace android {
-
-jni::Object<CategoricalStops> CategoricalStops::New(jni::JNIEnv& env, jni::Array<jni::Object<Stop>> stops) {
- static auto constructor = CategoricalStops::javaClass.GetConstructor<jni::Array<jni::Object<Stop>>>(env);
- return CategoricalStops::javaClass.New(env, constructor, stops);
-}
-
-jni::Class<CategoricalStops> CategoricalStops::javaClass;
-
-void CategoricalStops::registerNative(jni::JNIEnv& env) {
- CategoricalStops::javaClass = *jni::Class<CategoricalStops>::Find(env).NewGlobalRef(env).release();
-}
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/categorical_stops.hpp b/platform/android/src/style/functions/categorical_stops.hpp
deleted file mode 100644
index a198c8d5c9..0000000000
--- a/platform/android/src/style/functions/categorical_stops.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
-#include "stop.hpp"
-
-namespace mbgl {
-namespace android {
-
-class CategoricalStops : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/CategoricalStops"; };
-
- static jni::Object<CategoricalStops> New(jni::JNIEnv&, jni::Array<jni::Object<Stop>>);
-
- static jni::Class<CategoricalStops> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-};
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/exponential_stops.cpp b/platform/android/src/style/functions/exponential_stops.cpp
deleted file mode 100644
index 6390a0ec35..0000000000
--- a/platform/android/src/style/functions/exponential_stops.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "exponential_stops.hpp"
-
-namespace mbgl {
-namespace android {
-
-jni::Object<ExponentialStops> ExponentialStops::New(jni::JNIEnv& env, jni::Object<java::lang::Float> base, jni::Array<jni::Object<Stop>> stops) {
- static auto constructor = ExponentialStops::javaClass.GetConstructor<jni::Object<java::lang::Float>, jni::Array<jni::Object<Stop>>>(env);
- return ExponentialStops::javaClass.New(env, constructor, base, stops);
-}
-
-jni::Class<ExponentialStops> ExponentialStops::javaClass;
-
-void ExponentialStops::registerNative(jni::JNIEnv& env) {
- ExponentialStops::javaClass = *jni::Class<ExponentialStops>::Find(env).NewGlobalRef(env).release();
-}
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/exponential_stops.hpp b/platform/android/src/style/functions/exponential_stops.hpp
deleted file mode 100644
index 391d723cef..0000000000
--- a/platform/android/src/style/functions/exponential_stops.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
-#include "../../java/lang.hpp"
-#include "stop.hpp"
-
-namespace mbgl {
-namespace android {
-
-class ExponentialStops : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/ExponentialStops"; };
-
- static jni::Object<ExponentialStops> New(jni::JNIEnv&, jni::Object<java::lang::Float>, jni::Array<jni::Object<Stop>>);
-
- static jni::Class<ExponentialStops> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-};
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/identity_stops.cpp b/platform/android/src/style/functions/identity_stops.cpp
deleted file mode 100644
index 239b0ddb88..0000000000
--- a/platform/android/src/style/functions/identity_stops.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "identity_stops.hpp"
-
-namespace mbgl {
-namespace android {
-
-jni::Object<IdentityStops> IdentityStops::New(jni::JNIEnv& env) {
- static auto constructor = IdentityStops::javaClass.GetConstructor<>(env);
- return IdentityStops::javaClass.New(env, constructor);
-}
-
-jni::Class<IdentityStops> IdentityStops::javaClass;
-
-void IdentityStops::registerNative(jni::JNIEnv& env) {
- IdentityStops::javaClass = *jni::Class<IdentityStops>::Find(env).NewGlobalRef(env).release();
-}
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/identity_stops.hpp b/platform/android/src/style/functions/identity_stops.hpp
deleted file mode 100644
index 150b2135f0..0000000000
--- a/platform/android/src/style/functions/identity_stops.hpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
-namespace mbgl {
-namespace android {
-
-class IdentityStops : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/IdentityStops"; };
-
- static jni::Object<IdentityStops> New(jni::JNIEnv&);
-
- static jni::Class<IdentityStops> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-};
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/interval_stops.cpp b/platform/android/src/style/functions/interval_stops.cpp
deleted file mode 100644
index c3d9b6513f..0000000000
--- a/platform/android/src/style/functions/interval_stops.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-#include "interval_stops.hpp"
-
-namespace mbgl {
-namespace android {
-
-jni::Object<IntervalStops> IntervalStops::New(jni::JNIEnv& env, jni::Array<jni::Object<Stop>> stops) {
- static auto constructor = IntervalStops::javaClass.GetConstructor<jni::Array<jni::Object<Stop>>>(env);
- return IntervalStops::javaClass.New(env, constructor, stops);
-}
-
-jni::Class<IntervalStops> IntervalStops::javaClass;
-
-void IntervalStops::registerNative(jni::JNIEnv& env) {
- IntervalStops::javaClass = *jni::Class<IntervalStops>::Find(env).NewGlobalRef(env).release();
-}
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/interval_stops.hpp b/platform/android/src/style/functions/interval_stops.hpp
deleted file mode 100644
index e3f75159cf..0000000000
--- a/platform/android/src/style/functions/interval_stops.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
-#include "stop.hpp"
-
-namespace mbgl {
-namespace android {
-
-class IntervalStops : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/IntervalStops"; };
-
- static jni::Object<IntervalStops> New(jni::JNIEnv&, jni::Array<jni::Object<Stop>>);
-
- static jni::Class<IntervalStops> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-};
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/stop.cpp b/platform/android/src/style/functions/stop.cpp
deleted file mode 100644
index f9ed4b7368..0000000000
--- a/platform/android/src/style/functions/stop.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include "interval_stops.hpp"
-
-namespace mbgl {
-namespace android {
-
-jni::Object<Stop::CompositeValue> Stop::CompositeValue::New(jni::JNIEnv& env, jni::Object<java::lang::Number> zoom, jni::Object<> value) {
- static auto constructor = Stop::CompositeValue::javaClass.GetConstructor<jni::Object<java::lang::Number>, jni::Object<>>(env);
- return Stop::CompositeValue::javaClass.New(env, constructor, zoom, value);
-}
-
-jni::Class<Stop> Stop::javaClass;
-
-jni::Class<Stop::CompositeValue> Stop::CompositeValue::javaClass;
-
-void Stop::registerNative(jni::JNIEnv& env) {
- Stop::javaClass = *jni::Class<Stop>::Find(env).NewGlobalRef(env).release();
- Stop::CompositeValue::javaClass = *jni::Class<Stop::CompositeValue>::Find(env).NewGlobalRef(env).release();
-}
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/functions/stop.hpp b/platform/android/src/style/functions/stop.hpp
deleted file mode 100644
index 7c697db65d..0000000000
--- a/platform/android/src/style/functions/stop.hpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include <mbgl/util/noncopyable.hpp>
-#include <jni/jni.hpp>
-
-#include "../../java/lang.hpp"
-
-namespace mbgl {
-namespace android {
-
-class Stop : private mbgl::util::noncopyable {
-public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/Stop"; };
-
- template<class I, class O>
- static jni::Object<Stop> New(jni::JNIEnv& env, jni::Object<I> in, jni::Object<O> out) {
- static auto constructor = Stop::javaClass.GetConstructor<jni::Object<>, jni::Object<>>(env);
- return Stop::javaClass.New(env, constructor, (jni::Object<>) in, (jni::Object<>) out);
- }
-
- static jni::Class<Stop> javaClass;
-
- static void registerNative(jni::JNIEnv&);
-
- class CompositeValue : private mbgl::util::noncopyable {
- public:
- static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/functions/stops/Stop$CompositeValue"; };
-
- static jni::Object<Stop::CompositeValue> New(jni::JNIEnv&, jni::Object<java::lang::Number>, jni::Object<>);
-
- static jni::Class<Stop::CompositeValue> javaClass;
- };
-};
-
-} // namespace android
-} // namespace mbgl
diff --git a/platform/android/src/style/layers/custom_layer.cpp b/platform/android/src/style/layers/custom_layer.cpp
index 51a48520bf..61e74a9cf5 100644
--- a/platform/android/src/style/layers/custom_layer.cpp
+++ b/platform/android/src/style/layers/custom_layer.cpp
@@ -7,14 +7,10 @@
namespace mbgl {
namespace android {
- CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong initializeFunction, jni::jlong renderFunction, jni::jlong contextLostFunction, jni::jlong deinitializeFunction, jni::jlong context)
+ CustomLayer::CustomLayer(jni::JNIEnv& env, jni::String layerId, jni::jlong host)
: Layer(env, std::make_unique<mbgl::style::CustomLayer>(
jni::Make<std::string>(env, layerId),
- reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initializeFunction),
- reinterpret_cast<mbgl::style::CustomLayerRenderFunction>(renderFunction),
- reinterpret_cast<mbgl::style::CustomLayerContextLostFunction>(contextLostFunction),
- reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitializeFunction),
- reinterpret_cast<void*>(context))
+ std::unique_ptr<mbgl::style::CustomLayerHost>(reinterpret_cast<mbgl::style::CustomLayerHost*>(host)))
) {
}
@@ -53,7 +49,7 @@ namespace android {
// Register the peer
jni::RegisterNativePeer<CustomLayer>(
env, CustomLayer::javaClass, "nativePtr",
- std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong>,
+ std::make_unique<CustomLayer, JNIEnv&, jni::String, jni::jlong>,
"initialize",
"finalize",
METHOD(&CustomLayer::update, "nativeUpdate"));
diff --git a/platform/android/src/style/layers/custom_layer.hpp b/platform/android/src/style/layers/custom_layer.hpp
index 9e079c1288..7eb649d923 100644
--- a/platform/android/src/style/layers/custom_layer.hpp
+++ b/platform/android/src/style/layers/custom_layer.hpp
@@ -16,7 +16,7 @@ public:
static void registerNative(jni::JNIEnv&);
- CustomLayer(jni::JNIEnv&, jni::String, jni::jlong, jni::jlong, jni::jlong, jni::jlong, jni::jlong);
+ CustomLayer(jni::JNIEnv&, jni::String, jni::jlong);
CustomLayer(mbgl::Map&, mbgl::style::CustomLayer&);
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..b3d90faab7
--- /dev/null
+++ b/platform/android/src/style/layers/heatmap_layer.cpp
@@ -0,0 +1,145 @@
+// 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::getHeatmapColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ auto propertyValue = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getDefaultHeatmapColor();
+ }
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, propertyValue);
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+ 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::getHeatmapColor, "nativeGetHeatmapColor"),
+ 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..9e8908b062
--- /dev/null
+++ b/platform/android/src/style/layers/heatmap_layer.hpp
@@ -0,0 +1,52 @@
+// 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> getHeatmapColor(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..6fe6e3cb29 100644
--- a/platform/android/src/style/layers/layer.cpp
+++ b/platform/android/src/style/layers/layer.cpp
@@ -4,16 +4,28 @@
#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>
// C++ -> Java conversion
#include "../conversion/property_value.hpp"
+#include <mbgl/style/filter.hpp>
#include <string>
@@ -78,10 +90,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 +99,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 +113,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 +125,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;
@@ -128,12 +135,45 @@ namespace android {
layer.accept(SetFilterEvaluator {std::move(*converted)});
}
+ struct GetFilterEvaluator {
+ mbgl::style::Filter noop(std::string layerType) {
+ Log::Warning(mbgl::Event::JNI, "%s doesn't support filter", layerType.c_str());
+ return {};
+ }
+
+ mbgl::style::Filter operator()(style::BackgroundLayer&) { return noop("BackgroundLayer"); }
+ mbgl::style::Filter operator()(style::CustomLayer&) { return noop("CustomLayer"); }
+ mbgl::style::Filter operator()(style::RasterLayer&) { return noop("RasterLayer"); }
+ mbgl::style::Filter operator()(style::HillshadeLayer&) { return noop("HillshadeLayer"); }
+
+ template <class LayerType>
+ mbgl::style::Filter operator()(LayerType& layer) {
+ return layer.getFilter();
+ }
+ };
+
+ jni::Object<gson::JsonElement> Layer::getFilter(jni::JNIEnv& env) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+
+ Filter filter = layer.accept(GetFilterEvaluator());
+
+ jni::Object<gson::JsonElement> converted;
+ if (filter.is<ExpressionFilter>()) {
+ ExpressionFilter filterExpression = filter.get<ExpressionFilter>();
+ mbgl::Value expressionValue = filterExpression.expression.get()->serialize();
+ converted = gson::JsonElement::New(env, expressionValue);
+ }
+ return converted;
+ }
+
struct SetSourceLayerEvaluator {
std::string sourceLayer;
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 +194,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) {
@@ -200,6 +241,7 @@ namespace android {
METHOD(&Layer::setLayoutProperty, "nativeSetLayoutProperty"),
METHOD(&Layer::setPaintProperty, "nativeSetPaintProperty"),
METHOD(&Layer::setFilter, "nativeSetFilter"),
+ METHOD(&Layer::getFilter, "nativeGetFilter"),
METHOD(&Layer::setSourceLayer, "nativeSetSourceLayer"),
METHOD(&Layer::getSourceLayer, "nativeGetSourceLayer"),
METHOD(&Layer::getMinZoom, "nativeGetMinZoom"),
diff --git a/platform/android/src/style/layers/layer.cpp.ejs b/platform/android/src/style/layers/layer.cpp.ejs
index 1debb096a3..b08f0ec4dc 100644
--- a/platform/android/src/style/layers/layer.cpp.ejs
+++ b/platform/android/src/style/layers/layer.cpp.ejs
@@ -48,12 +48,25 @@ namespace android {
// Property getters
<% for (const property of properties) { -%>
+<% if (property.name != 'heatmap-color') { -%>
jni::Object<jni::ObjectTag> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::<%- camelize(type) %>Layer>()-><%- camelize(type) %>Layer::get<%- camelize(property.name) %>());
return jni::Object<jni::ObjectTag>(*converted);
}
+<% } else { -%>
+ jni::Object<jni::ObjectTag> HeatmapLayer::getHeatmapColor(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ auto propertyValue = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getHeatmapColor();
+ if (propertyValue.isUndefined()) {
+ propertyValue = layer.as<mbgl::style::HeatmapLayer>()->HeatmapLayer::getDefaultHeatmapColor();
+ }
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, propertyValue);
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
+<% } -%>
<% if (property.transition) { -%>
jni::Object<TransitionOptions> <%- camelize(type) %>Layer::get<%- camelize(property.name) %>Transition(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
diff --git a/platform/android/src/style/layers/layer.hpp b/platform/android/src/style/layers/layer.hpp
index 78c3f80b48..2486b0dfa6 100644
--- a/platform/android/src/style/layers/layer.hpp
+++ b/platform/android/src/style/layers/layer.hpp
@@ -3,8 +3,9 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/style/layer.hpp>
-
+#include "../../gson/json_array.hpp"
#include "../value.hpp"
+#include "../../gson/json_element.hpp"
#include <jni/jni.hpp>
@@ -68,6 +69,8 @@ public:
void setFilter(jni::JNIEnv&, jni::Array<jni::Object<>>);
+ jni::Object<gson::JsonElement> getFilter(jni::JNIEnv&);
+
void setSourceLayer(jni::JNIEnv&, jni::String);
jni::String getSourceLayer(jni::JNIEnv&);
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 e28a7862f8..249387ea51 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;
@@ -50,9 +52,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 c600580119..6021a03dc3 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();
@@ -29,9 +29,11 @@ public:
jni::String getURL(jni::JNIEnv&);
void setImage(jni::JNIEnv&, jni::Object<Bitmap>);
+
void setCoordinates(jni::JNIEnv&, jni::Object<LatLngQuad>);
- 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..2ae6602224 100644
--- a/platform/darwin/docs/guides/For Style Authors.md.ejs
+++ b/platform/darwin/docs/guides/For Style Authors.md.ejs
@@ -178,21 +178,22 @@ source object is a member of one of the following subclasses of `MGLSource`:
In style JSON | In the SDK
--------------|-----------
+`vector` | `MGLVectorTileSource`
+`raster` | `MGLRasterTileSource`
+`raster-dem` | `MGLRasterDEMSource`
`geojson` | `MGLShapeSource`
-`raster` | `MGLRasterSource`
-`vector` | `MGLVectorSource`
`image` | `MGLImageSource`
`canvas` and `video` sources are not supported.
### Tile sources
-Raster and vector sources may be defined in TileJSON configuration files. This
-SDK supports the properties defined in the style specification, which are a
+Raster and vector tile sources may be defined in TileJSON configuration files.
+This SDK supports the properties defined in the style specification, which are a
subset of the keys defined in version 2.1.0 of the
[TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
specification. As an alternative to authoring a custom TileJSON file, you may
-supply various tile source options when creating a raster or vector source.
+supply various tile source options when creating a raster or vector tile source.
These options are detailed in the `MGLTileSourceOption` documentation:
In style JSON | In TileJSON | In the SDK
@@ -205,6 +206,7 @@ In style JSON | In TileJSON | In the SDK
`tileSize` | — | `MGLTileSourceOptionTileSize`
`attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security)
`scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem`
+`encoding` | – | `MGLTileSourceOptionDEMEncoding`
### Shape sources
@@ -265,12 +267,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 +287,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,42 +320,93 @@ 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
+
+Many expression operators defined in the style specification have corresponding
+symbols to be used with the `+[NSExpression expressionWithFormat:]`,
+`+[NSExpression expressionForFunction:arguments:]`, or
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method:
+
+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:` | `CAST(zipCode, 'NSNumber')`
+`to-string` | `stringValue` | `CAST(ele, 'NSString')`
+`typeof` | |
+`geometry-type` | `NSExpression.geometryTypeVariableExpression` | `$geometryType`
+`id` | `NSExpression.featureIdentifierVariableExpression` | `$featureIdentifier`
+`properties` | `NSExpression.featureAttributesVariableExpression` | `$featureAttributes`
+`at` | `objectFrom:withIndex:` | `array[n]`
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
+`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:]` or `MGL_IF` or `+[NSExpression mgl_expressionForConditional:trueExpression:falseExpresssion:]` | `TERNARY(1 = 2, YES, NO)` or `MGL_IF(1 = 2, YES, 2 = 2, YES, NO)`
+`coalesce` | `mgl_coalesce:` | `mgl_coalesce({x, y, z})`
+`match` | `MGL_MATCH` or `+[NSExpression mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')`
+`interpolate` | `mgl_interpolate:withCurveType:parameters:stops:` or `+[NSExpression mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` |
+`step` | `mgl_step:withMinimum:stops:` or `+[NSExpression mgl_expressionForSteppingExpression:fromExpression:stops:]` |
+`let` | `mgl_expressionWithContext:` | `MGL_LET('ios', 11, 'macos', 10.13, $ios + $macos)`
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `mgl_join:` or `-[NSExpression mgl_expressionByAppendingExpression:]` | `mgl_join({'Old', ' ', 'MacDonald'})`
+`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`
+`abs` | `abs:` | `abs(-1)`
+`acos` | `mgl_acos:` | `mgl_acos(1)`
+`asin` | `mgl_asin:` | `mgl_asin(0)`
+`atan` | `mgl_atan:` | `mgl_atan(20)`
+`ceil` | `ceiling:` | `ceiling(0.99999)`
+`cos` | `mgl_cos:` | `mgl_cos(0)`
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`floor` | `floor:` | `floor(-0.99999)`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | `mgl_log2:` | `mgl_log2(1024)`
+`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`
+`round` | `mgl_round:` | `mgl_round(1.5)`
+`sin` | `mgl_sin:` | `mgl_sin(0)`
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | `mgl_tan:` | `mgl_tan(0)`
+`zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoom`
+`heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity`
+
+For operators that have no corresponding `NSExpression` symbol, use the
+`MGL_FUNCTION()` format string syntax.
## Filtering sources
-You can filter a shape or vector source by setting the
+You can filter a shape or vector tile source by setting the
`MGLVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a
table of style JSON operators and the corresponding operators used in the
predicate format string:
@@ -370,5 +427,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/Migrating to Expressions.md.ejs b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs
new file mode 100644
index 0000000000..addbf6940e
--- /dev/null
+++ b/platform/darwin/docs/guides/Migrating to Expressions.md.ejs
@@ -0,0 +1,212 @@
+<%
+ const os = locals.os;
+ const iOS = os === 'iOS';
+ const macOS = os === 'macOS';
+ const cocoaPrefix = iOS ? 'UI' : 'NS';
+ const guide = 'MigratingToExpressions';
+-%>
+<!--
+ This file is generated.
+ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+-->
+
+# Migrating from Style Functions to Expressions
+
+[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
+
+With Mapbox Maps SDK for <%- iOS ? 'iOS v4.0.0' : 'macOS v0.7.0' %>, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions.
+
+An expression is represented at runtime by the `NSExpression` class. Expressions can be used to style paint and layout properties based on zoom level, data attributes, or a combination of the two.
+
+A constant expression can also be assigned to a style property. For example, the opacity of a fill style layer can be set to a constant value between 0 and 1.
+
+The documentation for each individual style layer property notes which non-constant expressions are enabled for that property. Style functions supported four interpolation modes: exponential, interval, categorical, and identity.
+
+This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php). Under each interpolation mode, the style function implementation will be shown, followed by the current syntax.
+
+For more information about how to work with GeoJSON data in our <%- os %> SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. To learn more about supported expressions, see our ["Predicates and Expressions"](predicates-and-expressions.html) guide. The "Predicates and Expressions" guide also outlines Mapbox custom functions that can be used to dynamically style a map.
+
+## Stops
+Stops are dictionary keys that are associated with layer attribute values. Constant values no longer need to be wrapped as style values when they are values in a stops dictionary.
+
+
+Style function syntax:
+
+```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),
+]
+```
+
+Current syntax:
+<%- guideExample(guide, 'Stops', os) %>
+
+
+## Interpolation mode
+
+Style functions supported four interpolation modes: exponential/linear, interval, categorical, and identity. For more information about supported custom expressions, please see the "Predicates and Expressions" guide.
+
+### Linear
+
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` takes the interpolation type as a parameter. If you previously used the default interpolation base, use the curve type `MGLExpressionInterpolationMode.linear`. See the [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code) documentation for more details.
+
+The stops dictionary below, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
+
+Style function syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+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 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)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+Current syntax:
+
+<%- guideExample(guide, 'Linear', os) %>
+
+### Exponential
+
+If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect.
+
+Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key.
+
+<img src="img/data-driven-styling/exponential-function.png" height=344/>
+<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
+
+The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The interpolation base is `1.5`.
+
+Style function syntax:
+
+```swift
+let stops = [
+ 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 18),
+]
+
+layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
+ cameraStops: stops,
+ options: [.interpolationBase: 1.5])
+```
+
+Current syntax:
+
+<%- guideExample(guide, 'Exponential', os) %>
+
+### Interval
+
+Steps, or intervals, create a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the layout or paint value assigned to that key. You can use the `+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]` method or the custom function [`mgl_step:from:stops:`](predicates-and-expressions.html#code-mgl_step-from-stops-code) for cases where you previously used interval interpolation mode. The first parameter takes the feature attribute name and the second parameter (`from:`) optionally takes the default or fallback value for that function. The final parameter takes a stops dictionary as an argument.
+
+When we use the stops dictionary given above with an `'mgl_step:from:stops:'`, 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.
+
+Style function syntax:
+
+```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),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .interval,
+ sourceStops: stops,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+````
+
+Current syntax:
+
+<%- guideExample(guide, 'Interval', os) %>
+
+### Categorical
+
+Categorical interpolation mode took a stops dictionary. If the value for a specified feature attribute name matched one in that stops dictionary, the style value for that attribute value would be used. Categorical style functions can now be replaced with `MGL_MATCH`.
+
+`MGL_MATCH` takes an initial condition, which in this case is an attribute key. This is followed by possible matches for that key and the value to assign to the layer property if there is a match. The final argument can be a default style value that is to be used if none of the specified values match.
+
+There are three main types of events in the USGS 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.
+
+Style function syntax:
+
+```swift
+let categoricalStops = [
+ "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
+ "explosion": MGLStyleValue(rawValue: .red),
+ "quarry blast": MGLStyleValue(rawValue: .yellow),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
+ sourceStops: categoricalStops,
+ attributeName: "type",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+```
+
+Current syntax:
+<%- guideExample(guide, 'Categorical', os) %>
+
+If your use case does not require a default value, you can either apply a predicate to your layer prior to styling it, or use the format string `"valueForKeyPath:"`.
+
+### Identity
+
+Identity interpolation mode used the attribute’s value as the style layer property value. In this example, you might set the `circleRadius` to the earthquake’s magnitude. In order to use a feature attribute value to style a layer property, set the property value to `[NSExpression expressionForKeyPath:]`, which take the feature attribute name as an argument.
+
+Style function syntax:
+
+```swift
+layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
+ sourceStops: nil,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+```
+
+Current syntax:
+<%- guideExample(guide, 'Identity', os) %>
+
+![identity mode](img/data-driven-styling/identity.png)
+
+Some built-in functions can be applied to attribute values to style layer property values. To set the circle radius to three times the earthquake’s magnitude, create a `multiply:by:` function that takes the attribute value and the multiplier as arguments, or use a format string.
+
+<%- guideExample(guide, 'Multiply', os) %>
+
+![multiply magnitude](img/data-driven-styling/multiply.png)
+
+You can also cast attribute values in order to use them. One example is to cast an integer as an `NSString` and use it as a text value.
+
+<%- guideExample(guide, 'Cast', os) %>
+
+![cast a value](img/data-driven-styling/cast.png)
+
+### Constant Values
+
+For constant values that do not necessarily change based on camera or attribute values, use `[NSExpression expressionForConstantValue:]` (previously `[MGLStyleValue valueWithRawValue:]`).
+
+## Resources
+
+* [USGS Earthquake Feed](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
+* [For Style Authors](for-style-authors.html)
+* [Predicates and Expressions](predicates-and-expressions.html)
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..c3b3d39a52
--- /dev/null
+++ b/platform/darwin/docs/guides/Predicates and Expressions.md
@@ -0,0 +1,889 @@
+# 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.
+
+### Operators
+
+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 }`
+
+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.
+
+The following compound operators are supported:
+
+`NSCompoundPredicateType` | Format string syntax
+--------------------------|---------------------
+`NSAndPredicateType` | `predicate1 AND predicate2`<br />`predicate1 && predicate2`
+`NSOrPredicateType` | `predicate1 OR predicate2`<br /><code>predicate1 &vert;&vert; predicate2</code>
+`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`
+
+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.
+
+### Operands
+
+Operands in predicates can be [variables](#variables), [key paths](#key-paths),
+or almost anything else that can appear
+[inside an expression](#using-expressions-to-configure-layout-and-paint-attributes).
+
+Automatic type casting is not performed. Therefore, a feature only matches a
+predicate if its value for the attribute in question is of the same type as the
+value specified in the predicate. Use the `CAST()` operator to convert a key
+path or variable into a matching type:
+
+* To cast a value to a number, use `CAST(key, 'NSNumber')`.
+* To cast a value to a string, use `CAST(key, 'NSString')`.
+
+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.
+
+## Using expressions to configure layout and paint attributes
+
+An expression can contain subexpressions of various types. Each of the supported
+types of expressions is discussed below.
+
+### Constant values
+
+A constant value can be of any of the following types:
+
+In Objective-C | In Swift
+----------------------|---------
+`NSColor` (macOS)<br>`UIColor` (iOS) | `NSColor` (macOS)<br>`UIColor` (iOS)
+`NSString` | `String`
+`NSString` | `String`
+`NSNumber.boolValue` | `NSNumber.boolValue`
+`NSNumber.doubleValue` | `NSNumber.doubleValue`
+`NSArray<NSNumber>` | `[Float]`
+`NSArray<NSString>` | `[String]`
+`NSValue.CGVectorValue` (iOS)<br>`NSValue` containing `CGVector` (macOS) | `NSValue.cgVectorValue` (iOS)<br>`NSValue` containing `CGVector` (macOS)
+`NSValue.UIEdgeInsetsValue` (iOS)<br>`NSValue.edgeInsetsValue` (macOS) | `NSValue.uiEdgeInsetsValue` (iOS)<br>`NSValue.edgeInsetsValue` (macOS)
+
+For literal floating-point values, use `-[NSNumber numberWithDouble:]` instead
+of `-[NSNumber numberWithFloat:]` to avoid precision issues.
+
+### 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.
+
+The following special attribute is also available on features that are produced
+as a result of clustering multiple point features together in a shape source:
+
+<table>
+<thead>
+<tr><th>Attribute</th><th>Type</th><th>Meaning</th></tr>
+</thead>
+<tbody>
+<tr>
+ <td><code>point_count</code></td>
+ <td>Number</td>
+ <td>The number of point features in a given cluster.</td>
+</tr>
+</tbody>
+</table>
+
+Some characters may not be used directly as part of a key path in a format
+string. For example, if a feature’s attribute is named `ISO 3166-1:2006`, an
+expression format string of `lowercase(ISO 3166-1:2006)` or a predicate format
+string of `ISO 3166-1:2006 == 'US-OH'` would raise an exception. Instead, use a
+`%K` placeholder or the `+[NSExpression expressionForKeyPath:]` initializer:
+
+```objc
+[NSPredicate predicateWithFormat:@"%K == 'US-OH'", @"ISO 3166-1:2006"];
+[NSExpression expressionForFunction:@"lowercase:"
+ arguments:@[[NSExpression expressionForKeyPath:@"ISO 3166-1:2006"]]]
+```
+
+```swift
+NSPredicate(format: "%K == 'US-OH'", "ISO 3166-1:2006")
+NSExpression(forFunction: "lowercase:",
+ arguments: [NSExpression(forKeyPath: "ISO 3166-1:2006")])
+```
+
+### Functions
+
+Of the
+[functions predefined](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init#discussion)
+by the
+[`+[NSExpression expressionForFunction:arguments:]` method](https://developer.apple.com/documentation/foundation/nsexpression/1413747-init),
+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)`
+`length:` | `length('Wapakoneta')`
+`castObject:toType:` | `CAST(ele, 'NSString')`<br>`CAST(ele, 'NSNumber')`
+
+A number of [Mapbox-specific functions](#mapbox-specific-functions) are also
+available.
+
+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)`
+
+### Conditionals
+
+Conditionals are supported via the built-in
+`+[NSExpression expressionForConditional:trueExpression:falseExpression:]`
+method and `TERNARY()` operator. If you need to express multiple cases
+(“else-if”), you can either nest a conditional within a conditional or use the
+[`MGL_IF()`](#code-mgl_if-code) or [`MGL_MATCH()`](#code-mgl_match-code) function.
+
+### Aggregates
+
+Aggregate expressions can contain arrays of expressions. In some cases, it is
+possible to use the array itself instead of wrapping the array in an aggregate
+expression.
+
+### 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>$featureIdentifier</code></td>
+ <td>
+ Any
+ <a href="working-with-geojson-data.html#about-geojson-deserialization">GeoJSON data type</a>
+ </td>
+ <td>
+ A value that uniquely identifies the feature in the containing source.
+ This variable corresponds to the
+ <code>NSExpression.featureIdentifierVariableExpression</code> property.
+ </td>
+</tr>
+<tr>
+ <td><code>$geometryType</code></td>
+ <td>String</td>
+ <td>
+ The type of geometry represented by the feature. A feature’s type is 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>
+ This variable corresponds to the
+ <code>NSExpression.geometryTypeVariableExpression</code> property.
+ </td>
+</tr>
+<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 <code>heatmapColor</code> property.
+ This variable corresponds to the
+ <code>NSExpression.heatmapDensityVariableExpression</code> 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. This variable corresponds to the
+ <code>NSExpression.zoomLevelVariableExpression</code> property.
+ </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:@"MGL_LET('floorCount', 2, $floorCount + 1)"];
+```
+
+```swift
+NSExpression(format: "MGL_LET(floorCount, 2, $floorCount + 1)")
+```
+
+## Mapbox-specific functions
+
+For compatibility with the Mapbox Style Specification, the following functions
+are defined by this SDK. When setting a style layer property, you can call these
+functions just like the predefined functions above, using either the
+`+[NSExpression expressionForFunction:arguments:]` method or a convenient format
+string syntax:
+
+### `mgl_does:have:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_does:have:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_does:have:(SELF, '🧀🍔')</code> or <code>mgl_does:have:(%@, '🧀🍔')</code></dd>
+</dl>
+
+Returns a Boolean value indicating whether the dictionary has a value for the
+key or whether the evaluated object (`SELF`) has a value for the feature
+attribute. Compared to the [`mgl_has:`](#code-mgl_has-code) custom function,
+that function’s target is instead passed in as the first argument to this
+function. Both functions are equivalent to the syntax `key != NIL` or
+`%@[key] != NIL` but can be used outside of a predicate.
+
+### `mgl_interpolate:withCurveType:parameters:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_interpolate:withCurveType:parameters:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)</code></dd>
+</dl>
+
+Produces continuous, smooth results by interpolating between pairs of input and
+output values (“stops”). Compared to the
+[`mgl_interpolateWithCurveType:parameters:stops:`](#code-mgl_interpolatewithcurvetype-parameters-stops-code)
+custom function, the input expression (that function’s target) is instead passed
+in as the first argument to this function.
+
+### `mgl_step:from:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_step:from:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_step:from:stops:(x, 11, %@)</code></dd>
+</dl>
+
+Produces discrete, stepped results by evaluating a piecewise-constant function
+defined by pairs of input and output values ("stops"). Compared to the
+[`mgl_stepWithMinimum:stops:`](#code-mgl_stepwithminimum-stops-code) custom
+function, the input expression (that function’s target) is instead passed in as
+the first argument to this function.
+
+### `mgl_join:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_join:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_join({'Old', 'MacDonald'})</code></dd>
+</dl>
+
+Returns the result of concatenating together all the elements of an array in
+order. Compared to the
+[`stringByAppendingString:`](#code-stringbyappendingstring-code) custom
+function, this function takes only one argument, which is an aggregate
+expression containing the strings to concatenate.
+
+### `mgl_acos:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_acos:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_acos(1)</code></dd>
+</dl>
+
+Returns the arccosine of the number.
+
+This function corresponds to the
+[`acos`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-acos)
+operator in the Mapbox Style Specification.
+
+### `mgl_asin:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_asin:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_asin(0)</code></dd>
+</dl>
+
+Returns the arcsine of the number.
+
+This function corresponds to the
+[`asin`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-asin)
+operator in the Mapbox Style Specification.
+
+### `mgl_atan:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_atan:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_atan(20)</code></dd>
+</dl>
+
+Returns the arctangent of the number.
+
+This function corresponds to the
+[`atan`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-atan)
+operator in the Mapbox Style Specification.
+
+### `mgl_cos:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_cos:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_cos(0)</code></dd>
+</dl>
+
+Returns the cosine of the number.
+
+This function corresponds to the
+[`cos`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-cos)
+operator in the Mapbox Style Specification.
+
+### `mgl_log2:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_log2:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_log2(1024)</code></dd>
+</dl>
+
+Returns the base-2 logarithm of the number.
+
+This function corresponds to the
+[`log2`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-log2)
+operator in the Mapbox Style Specification.
+
+### `mgl_round:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_round:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_round(1.5)</code></dd>
+</dl>
+
+Returns the number rounded to the nearest integer. If the number is halfway
+between two integers, this function rounds it away from zero.
+
+This function corresponds to the
+[`round`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-round)
+operator in the Mapbox Style Specification.
+
+### `mgl_sin:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_sin:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_sin(0)</code></dd>
+</dl>
+
+Returns the sine of the number.
+
+This function corresponds to the
+[`sin`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-sin)
+operator in the Mapbox Style Specification.
+
+### `mgl_tan:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_tan:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_tan(0)</code></dd>
+</dl>
+
+Returns the tangent of the number.
+
+This function corresponds to the
+[`tan`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-tan)
+operator in the Mapbox Style Specification.
+
+### `mgl_coalesce:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_coalesce:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>mgl_coalesce({x, y, z})</code></dd>
+</dl>
+
+Returns the first non-`nil` value from an array of expressions.
+
+This function corresponds to the
+[`coalesce`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-coalesce)
+operator in the Mapbox Style Specification.
+
+### `MGL_LET`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_LET:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_LET('age', uppercase('old'), 'name', uppercase('MacDonald'), mgl_join({$age, $name}))</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Any number of variable names interspersed with their assigned
+ <code>NSExpression</code> values, followed by an <code>NSExpression</code>
+ that may contain references to those variables.
+</dd>
+</dl>
+
+Returns the result of evaluating an expression with the given variable values.
+Compared to the
+[`mgl_expressionWithContext:`](#code-mgl_expressionwithcontext-code) custom
+function, this function takes the variable names and values inline before the
+expression that contains references to those variables.
+
+### `MGL_MATCH`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_MATCH:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ An input expression, then any number of argument pairs, followed by a default
+ expression. Each argument pair consists of a constant value followed by an
+ expression to produce as a result of matching that constant value.
+</dd>
+</dl>
+
+Returns the result of matching the input expression against the given constant
+values.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]`
+method and the
+[`match`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-match)
+operator in the Mapbox Style Specification.
+
+### `MGL_IF`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_IF:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_IF(1 = 2, YES, 2 = 2, YES, NO)</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Alternating <code>NSPredicate</code> conditionals and resulting expressions,
+ followed by a default expression.
+</dd>
+</dl>
+
+Returns the first expression that meets the condition; otherwise, the default
+value. Unlike
+`+[NSExpression expressionForConditional:trueExpression:falseExpression:]` or
+the `TERNARY()` syntax, this function can accept multiple “if else” conditions
+and is supported on iOS 8._x_ and macOS 10.10._x_; however, each conditional
+passed into this function must be wrapped in a constant expression.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForConditional:trueExpression:falseExpresssion:]`
+method and the
+[`case`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-case)
+operator in the Mapbox Style Specification.
+
+### `MGL_FUNCTION`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>MGL_FUNCTION:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>MGL_FUNCTION('typeof', mystery)</code></dd>
+<dt>Arguments:</dt>
+<dd>
+ Any arguments required by the expression operator.
+</dd>
+</dl>
+
+An expression exactly as defined by the
+[Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions).
+
+## Custom functions
+
+The following custom functions are also available with the
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method or the
+`FUNCTION()` format string syntax.
+
+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.
+
+The Mapbox Style Specification defines some operators for which no custom
+function is available. To use these operators in an `NSExpression`, call the
+[`MGL_FUNCTION()`](#code-mgl_function-code) function with the same arguments
+that the operator expects.
+
+### `boolValue`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>boolValue</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(1, 'boolValue')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number or string.
+</dd>
+<dt>Arguments:</dt>
+<dd>None.</dd>
+</dl>
+
+A Boolean representation of the target: <code>FALSE</code> when then input is an
+empty string, 0, <code>FALSE</code>, <code>NIL</code>, or <code>NaN</code>,
+otherwise <code>TRUE</code>.
+
+### `mgl_has:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_has:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION($featureAttributes, 'mgl_has:', '🧀🍔')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to an <code>NSDictionary</code>
+ or the evaluated object (<code>SELF</code>).
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to an <code>NSString</code>
+ representing the key to look up in the dictionary or the feature attribute to
+ look up in the evaluated object (see <code>MGLFeature.attributes</code>).
+</dd>
+</dl>
+
+<code>true</code> if the dictionary has a value for the key or if the evaluated
+object has a value for the feature attribute.
+
+This function corresponds to the
+[`has`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-has)
+operator in the Mapbox Style Specification. See also the
+[`mgl_does:have:`](#code-mgl_does-have-code) function, which is used on its own
+without the `FUNCTION()` operator. You can also check whether an object has an
+attribute by comparing the key path to `NIL`, for example `cheeseburger != NIL`
+or `burger.cheese != NIL`
+
+### `mgl_expressionWithContext:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_expressionWithContext:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($ios + $macos, 'mgl_expressionWithContext:', %@)</code> with
+ a dictionary containing <code>ios</code> and <code>macos</code> keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that may contain references to the variables
+ defined in the context dictionary.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ An <code>NSDictionary</code> with <code>NSString</code>s as keys and
+ <code>NSExpression</code>s as values. Each key is a variable name and each
+ value is the variable’s value within the target expression.
+</dd>
+</dl>
+
+The target expression with variable subexpressions replaced with the values
+defined in the context dictionary.
+
+This function corresponds to the
+[`let`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-let)
+operator in the Mapbox Style Specification. See also the
+[`MGL_LET`](#code-mgl_let-code) function, which is used on its own without the
+`FUNCTION()` operator.
+
+### `mgl_interpolateWithCurveType:parameters:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_interpolateWithCurveType:parameters:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($zoomLevel, 'mgl_interpolateWithCurveType:parameters:stops:', 'linear', NIL, %@)</code>
+ with a dictionary containing zoom levels or other constant values as keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number and contains a
+ variable or key path expression.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ The first argument is one of the following strings denoting curve types:
+ <code>linear</code>, <code>exponential</code>, or <code>cubic-bezier</code>.
+
+ The second argument is an expression providing parameters for the curve:
+
+ <ul>
+ <li>If the curve type is <code>linear</code>, the argument is <code>NIL</code>.</li>
+ <li>
+ If the curve type is <code>exponential</code>, the argument is an
+ expression that evaluates to a number, specifying the base of the
+ exponential interpolation.
+ </li>
+ <li>
+ If the curve type is <code>cubic-bezier</code>, 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 <code>NSDictionary</code> object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+</dd>
+</dl>
+
+A value interpolated along the continuous mathematical function defined by the
+arguments, with the target as the input to the function.
+
+The input expression is matched against the keys in the stop dictionary. The
+keys may be feature attribute values, zoom levels, or heatmap densities. The
+values may be constant values or `NSExpression` objects. 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.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]`
+method and the
+[`interpolate`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate)
+operator in the Mapbox Style Specification. See also the
+[`mgl_interpolate:withCurveType:parameters:stops:`](#code-mgl_interpolate-withcurvetype-parameters-stops-code)
+function, which is used on its own without the `FUNCTION()` operator.
+
+### `mgl_numberWithFallbackValues:`
+
+<dl>
+<dt>Selector:</dt>
+<dd>
+ <code>mgl_numberWithFallbackValues:</code>,
+ <code>doubleValue</code>,
+ <code>floatValue</code>, or
+ <code>decimalValue</code>
+</dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(ele, 'mgl_numberWithFallbackValues:', 0)</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a Boolean value, number, or
+ string.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ Zero or more <code>NSExpression</code>s, each evaluating to a Boolean value
+ or string.
+</dd>
+</dl>
+
+A numeric representation of the target:
+
+* If the target is <code>NIL</code> or <code>FALSE</code>, the result is 0.
+* If the target is true, the result is 1.
+ * If the target is a string, it is converted to a number as specified by the
+ “[ToNumber Applied to the String Type](https://tc39.github.io/ecma262/#sec-tonumber-applied-to-the-string-type)”
+ algorithm of the ECMAScript Language Specification.
+ * If multiple values are provided, each one is evaluated in order until the
+ first successful conversion is obtained.
+
+This function corresponds to the
+[`to-number`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-number)
+operator in the Mapbox Style Specification. You can also cast a value to a
+number by passing the value and the string `NSNumber` into the `CAST()`
+operator.
+
+### `mgl_stepWithMinimum:stops:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>mgl_stepWithMinimum:stops:</code></dd>
+<dt>Format string syntax:</dt>
+<dd>
+ <code>FUNCTION($zoomLevel, 'mgl_stepWithMinimum:stops:', 0, %@)</code> with
+ a dictionary with zoom levels or other constant values as keys
+</dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a number and contains a
+ variable or key path expression.
+</dd>
+<dt>Arguments:</dt>
+<dd>
+ 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 <code>NSDictionary</code> object representing the
+ interpolation’s stops, with numeric zoom levels as keys and expressions as
+ values.
+</dd>
+</dl>
+
+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.
+
+The input expression is matched against the keys in the stop dictionary. The
+keys may be feature attribute values, zoom levels, or heatmap densities. The
+values may be constant values or `NSExpression` objects. 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.
+
+This function corresponds to the
+`+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]`
+method and the
+[`step`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-step)
+operator in the Mapbox Style Specification.
+
+### `stringByAppendingString:`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>stringByAppendingString:</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')</code></dd>
+<dt>Target:</dt>
+<dd>An <code>NSExpression</code> that evaluates to a string.</dd>
+<dt>Arguments:</dt>
+<dd>One or more <code>NSExpression</code>s, each evaluating to a string.</dd>
+</dl>
+
+The target string with each of the argument strings appended in order.
+
+This function corresponds to the
+`-[NSExpression(MGLAdditions) mgl_expressionByAppendingExpression:]`
+method and is similar to the
+[`concat`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-concat)
+operator in the Mapbox Style Specification. See also the
+[`mgl_join:`](#code-mgl_join-code) function, which concatenates multiple
+expressions and is used on its own without the `FUNCTION()` operator.
+
+### `stringValue`
+
+<dl>
+<dt>Selector:</dt>
+<dd><code>stringValue</code></dd>
+<dt>Format string syntax:</dt>
+<dd><code>FUNCTION(ele, 'stringValue')</code></dd>
+<dt>Target:</dt>
+<dd>
+ An <code>NSExpression</code> that evaluates to a Boolean value, number, or
+ string.
+</dd>
+<dt>Arguments:</dt>
+<dd>None.</dd>
+</dl>
+
+A string representation of the target:
+
+* If the target is <code>NIL</code>, the result is the empty string.
+* If the target is a Boolean value, the result is the string `true` or `false`.
+* If the target is a number, it is converted to a string as specified by the
+ “[NumberToString](https://tc39.github.io/ecma262/#sec-tostring-applied-to-the-number-type)”
+ algorithm of the ECMAScript Language Specification.
+* 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.
+* 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.
+
+This function corresponds to the
+[`to-string`](https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-to-string)
+operator in the Mapbox Style Specification. You can also cast a value to a
+string by passing the value and the string `NSString` into the `CAST()`
+operator.
diff --git a/platform/darwin/docs/guides/Tile URL Templates.md.ejs b/platform/darwin/docs/guides/Tile URL Templates.md.ejs
index 78fb297138..2b1de65b42 100644
--- a/platform/darwin/docs/guides/Tile URL Templates.md.ejs
+++ b/platform/darwin/docs/guides/Tile URL Templates.md.ejs
@@ -10,12 +10,12 @@
-->
# Tile URL Templates
-`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource`
-objects, can be created using an initializer that accepts an array of tile URL
-templates. Tile URL templates are strings that specify the URLs of the vector
-tiles or raster tile images to load. A template resembles an absolute URL, but
-with any number of placeholder strings that the source evaluates based on the
-tile it needs to load. For example:
+`MGLTileSource` objects, specifically `MGLRasterTileSource` and
+`MGLVectorTileSource` objects, can be created using an initializer that accepts
+an array of tile URL templates. Tile URL templates are strings that specify the
+URLs of the vector tiles or raster tile images to load. A template resembles an
+absolute URL, but with any number of placeholder strings that the source
+evaluates based on the tile it needs to load. For example:
* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be
evaluated as `http://www.example.com/tiles/14/6/9.pbf`.
@@ -62,7 +62,7 @@ all of which are optional:
<td>The tile’s zoom level. At zoom level 0, each tile covers the entire
world map; at zoom level 1, it covers ¼ of the world; at zoom level 2,
<sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by
- a <code>MGLRasterSource</code> object, whether the tile zoom level
+ a <code>MGLRasterTileSource</code> object, whether the tile zoom level
matches the map’s current zoom level depends on the value of the
source’s tile size as specified in the
<code>MGLTileSourceOptionTileSize</code> key of the <code>options</code>
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
deleted file mode 100644
index e41ac1c832..0000000000
--- a/platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs
+++ /dev/null
@@ -1,99 +0,0 @@
-<%
- const os = locals.os;
- const iOS = os === 'iOS';
- const macOS = os === 'macOS';
- const cocoaPrefix = iOS ? 'UI' : 'NS';
- const guide = 'UsingStyleFunctionsAtRuntime';
--%>
-<!--
- This file is generated.
- Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
--->
-
-# Using Style Functions at Runtime
-
-[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
-
-Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price.
-
-_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source.
-
-You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details.
-
-![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png)
-
-This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide.
-
-A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`:
-
-* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level.
-* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake.
-* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake.
-
-The documentation for each individual style layer property notes which style functions are enabled for that property.
-
-## 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.
-
-<%- guideExample(guide, 'Stops', os) %>
-
-## Interpolation mode
-
-The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function.
-
-### Linear
-
-`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary.
-
-The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
-
-<%- guideExample(guide, 'Linear', os) %>
-
-![exponential mode](img/data-driven-styling/exponential.png)
-
-### Exponential
-
-By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect.
-
-Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom.
-
-<img src="img/data-driven-styling/exponential-function.png" height=344/>
-<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
-
-The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The `MGLStyleFunctionOptionInterpolationBase` is `1.5`.
-
-<%- guideExample(guide, 'Exponential', os) %>
-
-### Interval
-
-`MGLInterpolationModeInterval` creates a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the style value assigned to that key.
-
-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.
-
-<%- guideExample(guide, 'Interval', os) %>
-
-![interval mode](img/data-driven-styling/interval.png)
-
-### Categorical
-
-At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes.
-
-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.
-
-<%- guideExample(guide, 'Categorical', os) %>
-
-![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
-
-### Identity
-
-`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`.
-
-<%- guideExample(guide, 'Identity', os) %>
-
-![identity mode](img/data-driven-styling/identity.png)
-
-##Resources
-
-* [USGS](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
-* [For Style Authors](for-style-authors.html)
diff --git a/platform/darwin/docs/img/data-driven-styling/cast.png b/platform/darwin/docs/img/data-driven-styling/cast.png
new file mode 100644
index 0000000000..e5b40b4ffa
--- /dev/null
+++ b/platform/darwin/docs/img/data-driven-styling/cast.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/categorical1.png b/platform/darwin/docs/img/data-driven-styling/categorical1.png
deleted file mode 100644
index 969846b41b..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/categorical1.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/categorical2.png b/platform/darwin/docs/img/data-driven-styling/categorical2.png
deleted file mode 100644
index 5008c522ed..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/categorical2.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/citibikes.png b/platform/darwin/docs/img/data-driven-styling/citibikes.png
deleted file mode 100644
index a616672a32..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/citibikes.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
index 6aa129a305..39a8a2a1e4 100644
--- a/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
+++ b/platform/darwin/docs/img/data-driven-styling/exponential-function-1.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential-function.png b/platform/darwin/docs/img/data-driven-styling/exponential-function.png
index c14969f0a8..f9824fc5e4 100644
--- a/platform/darwin/docs/img/data-driven-styling/exponential-function.png
+++ b/platform/darwin/docs/img/data-driven-styling/exponential-function.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/exponential.png b/platform/darwin/docs/img/data-driven-styling/exponential.png
deleted file mode 100644
index 87ddc1350e..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/exponential.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/identity.png b/platform/darwin/docs/img/data-driven-styling/identity.png
index 632ccdf3d5..3355694ec9 100644
--- a/platform/darwin/docs/img/data-driven-styling/identity.png
+++ b/platform/darwin/docs/img/data-driven-styling/identity.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/interval.png b/platform/darwin/docs/img/data-driven-styling/interval.png
deleted file mode 100644
index d15aff2025..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/interval.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/multiply.png b/platform/darwin/docs/img/data-driven-styling/multiply.png
new file mode 100644
index 0000000000..df42f243e1
--- /dev/null
+++ b/platform/darwin/docs/img/data-driven-styling/multiply.png
Binary files differ
diff --git a/platform/darwin/docs/img/data-driven-styling/polylineExample.png b/platform/darwin/docs/img/data-driven-styling/polylineExample.png
deleted file mode 100644
index cd9b39bae4..0000000000
--- a/platform/darwin/docs/img/data-driven-styling/polylineExample.png
+++ /dev/null
Binary files differ
diff --git a/platform/darwin/docs/theme/assets/css/jazzy.css.scss b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
index ad0a3b7082..ff6d2a374c 100644
--- a/platform/darwin/docs/theme/assets/css/jazzy.css.scss
+++ b/platform/darwin/docs/theme/assets/css/jazzy.css.scss
@@ -113,7 +113,12 @@ h2 {
}
h3 {
- @include heading(14px, 1em 0 0.3em);
+ @include heading(1.2rem, 1em 0 0.3em);
+}
+
+h3 > code {
+ font-weight: bold;
+ font-size: 1.2rem;
}
h4 {
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/da.lproj/Foundation.strings b/platform/darwin/resources/da.lproj/Foundation.strings
new file mode 100644
index 0000000000..d8d18f453c
--- /dev/null
+++ b/platform/darwin/resources/da.lproj/Foundation.strings
@@ -0,0 +1,297 @@
+/* Clock position format, long: {hours} o’clock */
+"CLOCK_FMT_LONG" = "%@ klokken";
+
+/* Clock position format, medium: {hours} o’clock */
+"CLOCK_FMT_MEDIUM" = "%@ klokken";
+
+/* Clock position format, short: {hours}:00 */
+"CLOCK_FMT_SHORT" = "%@:00";
+
+/* East, long */
+"COMPASS_E_LONG" = "øst";
+
+/* East, short */
+"COMPASS_E_SHORT" = "Ø";
+
+/* East by north, long */
+"COMPASS_EbN_LONG" = "øst via nord";
+
+/* East by north, short */
+"COMPASS_EbN_SHORT" = "ØvN";
+
+/* East by south, long */
+"COMPASS_EbS_LONG" = "øst via syd";
+
+/* East by south, short */
+"COMPASS_EbS_SHORT" = "ØvS";
+
+/* East-northeast, long */
+"COMPASS_ENE_LONG" = "øst-nordøst";
+
+/* East-northeast, short */
+"COMPASS_ENE_SHORT" = "ØNØ";
+
+/* East-southeast, long */
+"COMPASS_ESE_LONG" = "øst-sydøst";
+
+/* East-southeast, short */
+"COMPASS_ESE_SHORT" = "ØSØ";
+
+/* North, long */
+"COMPASS_N_LONG" = "nord";
+
+/* North, short */
+"COMPASS_N_SHORT" = "N";
+
+/* North by east, long */
+"COMPASS_NbE_LONG" = "nord via øst";
+
+/* North by east, short */
+"COMPASS_NbE_SHORT" = "NvØ";
+
+/* North by west, long */
+"COMPASS_NbW_LONG" = "nord via vest";
+
+/* North by west, short */
+"COMPASS_NbW_SHORT" = "NvV";
+
+/* Northeast, long */
+"COMPASS_NE_LONG" = "nordøst";
+
+/* Northeast, short */
+"COMPASS_NE_SHORT" = "NØ";
+
+/* Northeast by east, long */
+"COMPASS_NEbE_LONG" = "nordøst via øst";
+
+/* Northeast by east, short */
+"COMPASS_NEbE_SHORT" = "NØvØ";
+
+/* Northeast by north, long */
+"COMPASS_NEbN_LONG" = "nordøst via nord";
+
+/* Northeast by north, short */
+"COMPASS_NEbN_SHORT" = "NØvN";
+
+/* North-northeast, long */
+"COMPASS_NNE_LONG" = "nord-nordøst";
+
+/* North-northeast, short */
+"COMPASS_NNE_SHORT" = "NNØ";
+
+/* North-northwest, long */
+"COMPASS_NNW_LONG" = "nord-nordvest";
+
+/* North-northwest, short */
+"COMPASS_NNW_SHORT" = "NNV";
+
+/* Northwest, long */
+"COMPASS_NW_LONG" = "nordvest";
+
+/* Northwest, short */
+"COMPASS_NW_SHORT" = "NV";
+
+/* Northwest by north, long */
+"COMPASS_NWbN_LONG" = "nordvest via nord";
+
+/* Northwest by north, short */
+"COMPASS_NWbN_SHORT" = "NVvN";
+
+/* Northwest by west, long */
+"COMPASS_NWbW_LONG" = "nordvest via vest";
+
+/* Northwest by west, short */
+"COMPASS_NWbW_SHORT" = "NVvW";
+
+/* South, long */
+"COMPASS_S_LONG" = "syd";
+
+/* South, short */
+"COMPASS_S_SHORT" = "S";
+
+/* South by east, long */
+"COMPASS_SbE_LONG" = "syd via øst";
+
+/* South by east, short */
+"COMPASS_SbE_SHORT" = "SvØ";
+
+/* South by west, long */
+"COMPASS_SbW_LONG" = "syd via vest";
+
+/* South by west, short */
+"COMPASS_SbW_SHORT" = "SvV";
+
+/* Southeast, long */
+"COMPASS_SE_LONG" = "sydøst";
+
+/* Southeast, short */
+"COMPASS_SE_SHORT" = "SØ";
+
+/* Southeast by east, long */
+"COMPASS_SEbE_LONG" = "sydøst via øst";
+
+/* Southeast by east, short */
+"COMPASS_SEbE_SHORT" = "SØvØ";
+
+/* Southeast by south, long */
+"COMPASS_SEbS_LONG" = "sydøst via syd";
+
+/* Southeast by south, short */
+"COMPASS_SEbS_SHORT" = "SØvS";
+
+/* South-southeast, long */
+"COMPASS_SSE_LONG" = "syd-sydøst";
+
+/* South-southeast, short */
+"COMPASS_SSE_SHORT" = "SSØ";
+
+/* South-southwest, long */
+"COMPASS_SSW_LONG" = "syd-sydvest";
+
+/* South-southwest, short */
+"COMPASS_SSW_SHORT" = "SSV";
+
+/* Southwest, long */
+"COMPASS_SW_LONG" = "sydvest";
+
+/* Southwest, short */
+"COMPASS_SW_SHORT" = "SV";
+
+/* Southwest by south, long */
+"COMPASS_SWbS_LONG" = "sydvest via syd";
+
+/* Southwest by south, short */
+"COMPASS_SWbS_SHORT" = "SVvS";
+
+/* Southwest by west, long */
+"COMPASS_SWbW_LONG" = "sydvest via vest";
+
+/* Southwest by west, short */
+"COMPASS_SWbW_SHORT" = "SVvV";
+
+/* West, long */
+"COMPASS_W_LONG" = "vest";
+
+/* West, short */
+"COMPASS_W_SHORT" = "V";
+
+/* West by north, long */
+"COMPASS_WbN_LONG" = "vest via nord";
+
+/* West by north, short */
+"COMPASS_WbN_SHORT" = "VvN";
+
+/* West by south, long */
+"COMPASS_WbS_LONG" = "vest via syd";
+
+/* West by south, short */
+"COMPASS_WbS_SHORT" = "VvS";
+
+/* West-northwest, long */
+"COMPASS_WNW_LONG" = "vest-nordvest";
+
+/* West-northwest, short */
+"COMPASS_WNW_SHORT" = "VNV";
+
+/* West-southwest, long */
+"COMPASS_WSW_LONG" = "vest-sydvest";
+
+/* West-southwest, short */
+"COMPASS_WSW_SHORT" = "VSV";
+
+/* Degrees format, long */
+"COORD_DEG_LONG" = "%d grad(er)";
+
+/* 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$@ og %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$@, og %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" = "%@ øst";
+
+/* East longitude format, medium: {longitude} */
+"COORD_E_MEDIUM" = "%@ øst";
+
+/* East longitude format, short: {longitude} */
+"COORD_E_SHORT" = "%@Ø";
+
+/* Coordinate pair format, long: {latitude}, {longitude} */
+"COORD_FMT_LONG" = "%1$@ via %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 minut(ter)";
+
+/* Minutes format, medium: {minutes} */
+"COORD_MIN_MEDIUM" = "%d′";
+
+/* Minutes format, short: {minutes} */
+"COORD_MIN_SHORT" = "%d′";
+
+/* North latitude format, long: {latitude} */
+"COORD_N_LONG" = "%@ nord";
+
+/* North latitude format, medium: {latitude} */
+"COORD_N_MEDIUM" = "%@ nord";
+
+/* North latitude format, short: {latitude} */
+"COORD_N_SHORT" = "%@N";
+
+/* South latitude format, long: {latitude} */
+"COORD_S_LONG" = "%@ syd";
+
+/* South latitude format, medium: {latitude} */
+"COORD_S_MEDIUM" = "%@ syd";
+
+/* South latitude format, short: {latitude} */
+"COORD_S_SHORT" = "%@S";
+
+/* Seconds format, long */
+"COORD_SEC_LONG" = "%d sekunde(r)";
+
+/* Seconds format, medium: {seconds} */
+"COORD_SEC_MEDIUM" = "%d″";
+
+/* Seconds format, short: {seconds} */
+"COORD_SEC_SHORT" = "%d″";
+
+/* West longitude format, long: {longitude} */
+"COORD_W_LONG" = "%@ vest";
+
+/* West longitude format, medium: {longitude} */
+"COORD_W_MEDIUM" = "%@ vest";
+
+/* West longitude format, short: {longitude} */
+"COORD_W_SHORT" = "%@V";
+
+/* OpenStreetMap full name attribution */
+"OSM_FULL_NAME" = "OpenStreetMap";
+
+/* OpenStreetMap short name attribution */
+"OSM_SHORT_NAME" = "OSM";
+
diff --git a/platform/darwin/resources/da.lproj/Foundation.stringsdict b/platform/darwin/resources/da.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..17983b8aa8
--- /dev/null
+++ b/platform/darwin/resources/da.lproj/Foundation.stringsdict
@@ -0,0 +1,54 @@
+<?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>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d grad</string>
+ <key>other</key>
+ <string>%d grader</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d minut</string>
+ <key>other</key>
+ <string>%d minutter</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d sekund</string>
+ <key>other</key>
+ <string>%d sekunder</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
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/pt-PT.lproj/Foundation.stringsdict b/platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict
new file mode 100644
index 0000000000..e99e86d98d
--- /dev/null
+++ b/platform/darwin/resources/pt-PT.lproj/Foundation.stringsdict
@@ -0,0 +1,54 @@
+<?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>COORD_DEG_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@degrees@</string>
+ <key>degrees</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d grau</string>
+ <key>other</key>
+ <string>%d graus</string>
+ </dict>
+ </dict>
+ <key>COORD_MIN_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@minutes@</string>
+ <key>minutes</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d minuto</string>
+ <key>other</key>
+ <string>%d minutos</string>
+ </dict>
+ </dict>
+ <key>COORD_SEC_LONG</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@seconds@</string>
+ <key>seconds</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>d</string>
+ <key>one</key>
+ <string>%d segundo</string>
+ <key>other</key>
+ <string>%d segundos</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
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..c7b54b326a 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,34 @@ 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';
- 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';
- } else {
- if (property.function === "interpolated") {
- doc += '* `MGLCameraStyleFunction` with an interpolation mode of:\n' +
- ' * `MGLInterpolationModeExponential`\n' +
- ' * `MGLInterpolationModeInterval`\n';
+ doc += '\n\nYou can set this property to an expression containing any of the following:\n\n';
+ doc += `* Constant ${describeType(property)} values`;
+ if ('minimum' in property) {
+ if ('maximum' in property) {
+ doc += ` between ${formatNumber(property.minimum)} and ${formatNumber(property.maximum)} inclusive`;
} else {
- doc += '* `MGLCameraStyleFunction` with an interpolation mode of `MGLInterpolationModeInterval`\n';
+ doc += ` no less than ${formatNumber(property.minimum)}`;
}
+ } else if ('maximum' in property) {
+ doc += ` no greater than ${formatNumber(property.maximum)}`;
+ }
+ doc += '\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 += `* 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 {
+ 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 +340,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 +355,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 ' + formatNumber(value);
case 'string':
if (value === '') {
return 'the empty string';
@@ -349,21 +412,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) {
@@ -378,7 +438,7 @@ global.describeValue = function (value, property, layerType) {
if (color.r === 1 && color.g === 1 && color.b === 1 && color.a === 1) {
return '`UIColor.whiteColor`';
}
- return 'a `UIColor`' + ` object whose RGB value is ${color.r}, ${color.g}, ${color.b} and whose alpha value is ${color.a}`;
+ return 'a `UIColor`' + ` object whose RGB value is ${formatNumber(color.r)}, ${formatNumber(color.g)}, ${formatNumber(color.b)} and whose alpha value is ${formatNumber(color.a)}`;
case 'array':
let units = property.units || '';
if (units) {
@@ -389,12 +449,12 @@ global.describeValue = function (value, property, layerType) {
if (value[0] === 0 && value[1] === 0 && value[2] === 0 && value[3] === 0) {
return 'an `NSValue` object containing `UIEdgeInsetsZero`';
}
- return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${value[0]}${units} on the top, ${value[3]}${units} on the left, ${value[2]}${units} on the bottom, and ${value[1]}${units} on the right`;
+ return 'an `NSValue` object containing a `UIEdgeInsets` struct set to' + ` ${formatNumber(value[0])}${units} on the top, ${formatNumber(value[3])}${units} on the left, ${formatNumber(value[2])}${units} on the bottom, and ${formatNumber(value[1])}${units} on the right`;
case 'offset':
case 'translate':
- return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${value[0]}${units} rightward and ${value[1]}${units} downward`;
+ return 'an `NSValue` object containing a `CGVector` struct set to' + ` ${formatNumber(value[0])}${units} rightward and ${formatNumber(value[1])}${units} downward`;
case 'position':
- return 'an `MGLSphericalPosition` struct set to' + ` ${value[0]} radial, ${value[1]} azimuthal and ${value[2]} polar`;
+ return 'an `MGLSphericalPosition` struct set to' + ` ${formatNumber(value[0])} radial, ${formatNumber(value[1])} azimuthal and ${formatNumber(value[2])} polar`;
default:
return 'the array `' + value.join('`, `') + '`';
}
@@ -403,8 +463,16 @@ global.describeValue = function (value, property, layerType) {
}
};
+global.formatNumber = function (num) {
+ return num.toLocaleString().replace('-', '\u2212');
+}
+
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) {
@@ -558,15 +626,15 @@ const layerH = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.h.
const layerM = ejs.compile(fs.readFileSync('platform/darwin/src/MGLStyleLayer.mm.ejs', 'utf8'), { strict: true});
const testLayers = ejs.compile(fs.readFileSync('platform/darwin/test/MGLStyleLayerTests.mm.ejs', 'utf8'), { strict: true});
const forStyleAuthorsMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/For Style Authors.md.ejs', 'utf8'), { strict: true });
-const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Using Style Functions at Runtime.md.ejs', 'utf8'), { strict: true });
+const ddsGuideMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Migrating to Expressions.md.ejs', 'utf8'), { strict: true });
const templatesMD = ejs.compile(fs.readFileSync('platform/darwin/docs/guides/Tile URL Templates.md.ejs', 'utf8'), { strict: true });
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) => {
@@ -596,13 +664,13 @@ function duplicatePlatformDecls(src) {
// Look for a documentation comment that contains “MGLColor” or “UIColor”
// and the subsequent function, method, or property declaration. Try not to
// match greedily.
- return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\*\/)(\s*[^;]+?\b(?:MGL|NS|UI)(?:Color|EdgeInsets(?:Zero)?)\b[^;]+?;)/g,
+ return src.replace(/(\/\*\*(?:\*[^\/]|[^*])*?\b(?:MGL|NS|UI)Color\b[\s\S]*?\*\/)(\s*.+?;)/g,
(match, comment, decl) => {
- let macosComment = comment.replace(/\b(?:MGL|UI)(Color|EdgeInsets(?:Zero)?)\b/g, 'NS$1')
+ let macosComment = comment.replace(/\b(?:MGL|UI)Color\b/g, 'NSColor')
// Use the correct indefinite article.
- .replace(/\ba(\s+`?NS(?:Color|EdgeInsets))\b/gi, 'an$1');
- let iosDecl = decl.replace(/\bMGL(Color|EdgeInsets)\b/g, 'UI$1');
- let macosDecl = decl.replace(/\b(?:MGL|UI)(Color|EdgeInsets)\b/g, 'NS$1');
+ .replace(/\ba(\s+`?NSColor)\b/gi, 'an$1');
+ let iosDecl = decl.replace(/\bMGLColor\b/g, 'UIColor');
+ let macosDecl = decl.replace(/\b(?:MGL|UI)Color\b/g, 'NSColor');
return `\
#if TARGET_OS_IPHONE
${comment}${iosDecl}
@@ -639,9 +707,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 +747,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/Migrating to Expressions.md`, ddsGuideMD({
os: 'iOS',
}));
-fs.writeFileSync(`platform/macos/docs/guides/Using Style Functions at Runtime.md`, ddsGuideMD({
+writeIfModified(`platform/macos/docs/guides/Migrating to Expressions.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..d47b13cdb2 100644
--- a/platform/darwin/scripts/style-spec-overrides-v8.json
+++ b/platform/darwin/scripts/style-spec-overrides-v8.json
@@ -9,22 +9,28 @@
"type": {
"values": {
"fill": {
- "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLFillStyleLayer` is a style layer that renders one or more filled (and optionally stroked) polygons on the map.\n\nUse a fill style layer to configure the visual appearance of polygon or multipolygon features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"fill-extrusion": {
- "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLFillExtrusionStyleLayer` is a style layer that renders one or more 3D extruded polygons on the map.\n\nUse a fill-extrusion style layer to configure the visual appearance of polygon or multipolygon features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"line": {
- "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features in vector tiles loaded by an `MGLVectorSource` object or `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLLineStyleLayer` is a style layer that renders one or more stroked polylines on the map.\n\nUse a line style layer to configure the visual appearance of polyline or multipolyline features. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"symbol": {
- "doc": "An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at points or along lines on the map.\n\nUse a symbol style layer to configure the visual appearance of labels for features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or `MGLFeature` instances in an `MGLShapeSource` object."
+ "doc": "An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at points or along lines on the map.\n\nUse a symbol style layer to configure the visual appearance of feature labels. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLShape` or `MGLFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` object."
},
"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."
+ "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. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` 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. These features can come from vector tiles loaded by an `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or `MGLComputedShapeSource` 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 `MGLRasterTileSource` 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 `MGLVectorTileSource` 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 a style image to use for drawing an image background.\n\nUse the `+[MGLStyle setImage:forName:]` method to associate an image with a name that you can set this property to.\n\nWithin a constant string value, a feature attribute name enclosed in curly braces (e.g., `{token}`) is replaced with the value of the named attribute. Tokens inside non-constant expressions are ignored; instead, use `mgl_join:` and key path expressions."
+ },
+ "text-field": {
+ "doc": "Value to use for a text label.\n\nWithin a constant string value, a feature attribute name enclosed in curly braces (e.g., `{token}`) is replaced with the value of the named attribute. Tokens inside non-constant expressions are ignored; instead, use `mgl_join:` 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": "The color of each screen point based on its density value in a heatmap. This property is normally set to an interpolation or step expression with the `$heatmapDensity` value as its 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/MGLAccountManager.h b/platform/darwin/src/MGLAccountManager.h
index 741cc323cb..436e45ca9b 100644
--- a/platform/darwin/src/MGLAccountManager.h
+++ b/platform/darwin/src/MGLAccountManager.h
@@ -5,7 +5,7 @@
NS_ASSUME_NONNULL_BEGIN
/**
- The MGLAccountManager object provides a global way to set a Mapbox API access
+ The `MGLAccountManager` object provides a global way to set a Mapbox API access
token.
*/
MGL_EXPORT
@@ -14,9 +14,9 @@ MGL_EXPORT
#pragma mark Authorizing Access
/**
- Set the
+ The
<a href="https://www.mapbox.com/help/define-access-token/">Mapbox access token</a>
- to be used by all instances of MGLMapView in the current application.
+ used by all instances of `MGLMapView` in the current application.
Mapbox-hosted vector tiles and styles require an API access token, which you
can obtain from the
@@ -25,26 +25,18 @@ MGL_EXPORT
your Mapbox account. They also deter other developers from using your styles
without your permission.
- @param accessToken A Mapbox access token. Calling this method with a value of
- `nil` has no effect.
+ Setting this property to a value of `nil` has no effect.
@note You must set the access token before attempting to load any Mapbox-hosted
style. Therefore, you should generally set it before creating an instance of
- MGLMapView. The recommended way to set an access token is to add an entry to
- your application’s Info.plist file with the key `MGLMapboxAccessToken` and
- the type String. Alternatively, you may call this method from your
+ `MGLMapView`. The recommended way to set an access token is to add an entry
+ to your application’s Info.plist file with the key `MGLMapboxAccessToken`
+ and the type `String`. Alternatively, you may call this method from your
application delegate’s `-applicationDidFinishLaunching:` method.
*/
-+ (void)setAccessToken:(nullable NSString *)accessToken;
+@property (class, nullable) NSString *accessToken;
-/**
- Returns the
- <a href="https://www.mapbox.com/help/define-access-token/">Mapbox access token</a>
- in use by instances of MGLMapView in the current application.
- */
-+ (nullable NSString *)accessToken;
-
-+ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((deprecated("Telemetry settings are now always shown in the ℹ️ menu.")));
++ (BOOL)mapboxMetricsEnabledSettingShownInApp __attribute__((unavailable("Telemetry settings are now always shown in the ℹ️ menu.")));
@end
diff --git a/platform/darwin/src/MGLAccountManager.m b/platform/darwin/src/MGLAccountManager.m
index 7e55779d04..73da8ddd63 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];
});
@@ -54,11 +54,6 @@
return _sharedManager;
}
-+ (BOOL)mapboxMetricsEnabledSettingShownInApp {
- NSLog(@"mapboxMetricsEnabledSettingShownInApp is no longer necessary; the Mapbox Maps SDK for iOS has changed to always provide a telemetry setting in-app.");
- return YES;
-}
-
+ (void)setAccessToken:(NSString *)accessToken {
accessToken = [accessToken stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
@@ -69,9 +64,7 @@
[MGLAccountManager sharedManager].accessToken = accessToken;
#if TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
- // Update MGLMapboxEvents
- // NOTE: This is (likely) the initial setup of MGLMapboxEvents
- [MGLMapboxEvents sharedManager];
+ [MGLMapboxEvents setupWithAccessToken:accessToken];
#endif
}
diff --git a/platform/darwin/src/MGLAccountManager_Private.h b/platform/darwin/src/MGLAccountManager_Private.h
index 655af08f20..b68016b1a7 100644
--- a/platform/darwin/src/MGLAccountManager_Private.h
+++ b/platform/darwin/src/MGLAccountManager_Private.h
@@ -3,7 +3,7 @@
@interface MGLAccountManager (Private)
/// Returns the shared instance of the `MGLAccountManager` class.
-+ (instancetype)sharedManager;
+@property (class, nonatomic, readonly) MGLAccountManager *sharedManager;
/// The current global access token.
@property (atomic) NSString *accessToken;
diff --git a/platform/darwin/src/MGLAttributionInfo.mm b/platform/darwin/src/MGLAttributionInfo.mm
index 52a83fd18e..e8d6a203d0 100644
--- a/platform/darwin/src/MGLAttributionInfo.mm
+++ b/platform/darwin/src/MGLAttributionInfo.mm
@@ -16,7 +16,7 @@
@implementation MGLAttributionInfo
-+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
++ (NSArray<MGLAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
if (!htmlString) {
return @[];
}
@@ -49,7 +49,12 @@
CGFloat blue;
CGFloat alpha;
#if !TARGET_OS_IPHONE
- linkColor = [linkColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ // CSS uses the sRGB color space.
+ if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) {
+ linkColor = [linkColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ } else {
+ linkColor = [linkColor colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
+ }
#endif
[linkColor getRed:&red green:&green blue:&blue alpha:&alpha];
[css appendFormat:
@@ -107,7 +112,7 @@
return infos;
}
-+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos {
++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray<MGLAttributionInfo *> *)attributionInfos {
NSMutableArray *titles = [NSMutableArray arrayWithCapacity:attributionInfos.count];
for (MGLAttributionInfo *info in attributionInfos) {
NSMutableAttributedString *title = info.title.mutableCopy;
@@ -160,6 +165,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"]],
]];
}
}
@@ -253,7 +259,7 @@
}
}
-- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos {
+- (void)growArrayByAddingAttributionInfosFromArray:(NSArray<MGLAttributionInfo *> *)infos {
for (MGLAttributionInfo *info in infos) {
[self growArrayByAddingAttributionInfo:info];
}
diff --git a/platform/darwin/src/MGLAttributionInfo_Private.h b/platform/darwin/src/MGLAttributionInfo_Private.h
index c639752ac3..85c9ed796f 100644
--- a/platform/darwin/src/MGLAttributionInfo_Private.h
+++ b/platform/darwin/src/MGLAttributionInfo_Private.h
@@ -16,9 +16,9 @@ NS_ASSUME_NONNULL_BEGIN
@param fontSize The default text size in points.
@param linkColor The default link color.
*/
-+ (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
++ (NSArray<MGLAttributionInfo *> *)attributionInfosFromHTMLString:(nullable NSString *)htmlString fontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
-+ (NSAttributedString *)attributedStringForAttributionInfos:(NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos;
++ (NSAttributedString *)attributedStringForAttributionInfos:(NSArray<MGLAttributionInfo *> *)attributionInfos;
/**
Returns a copy of the `URL` property modified to account for the given style
@@ -58,7 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
@param infos An array of info objects to add to the receiver.
*/
-- (void)growArrayByAddingAttributionInfosFromArray:(NS_ARRAY_OF(MGLAttributionInfo *) *)infos;
+- (void)growArrayByAddingAttributionInfosFromArray:(NSArray<MGLAttributionInfo *> *)infos;
@end
diff --git a/platform/darwin/src/MGLBackgroundStyleLayer.h b/platform/darwin/src/MGLBackgroundStyleLayer.h
index 659903914c..d5c3ed5403 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
@@ -42,40 +41,48 @@ which it is added.
/**
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`
+ * 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) MGLStyleValue<UIColor *> *backgroundColor;
+@property (nonatomic, null_resettable) NSExpression *backgroundColor;
#else
/**
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
`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:
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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<NSColor *> *backgroundColor;
+@property (nonatomic, null_resettable) NSExpression *backgroundColor;
#endif
/**
@@ -88,18 +95,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 between 0 and 1 inclusive
+ * 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 +123,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..993645d3a8 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,22 @@
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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setBackgroundPatternTransition:(MGLTransition )transition {
diff --git a/platform/darwin/src/MGLCircleStyleLayer.h b/platform/darwin/src/MGLCircleStyleLayer.h
index ea95f6beef..06b4de32f0 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.
@@ -63,9 +62,10 @@ typedef NS_ENUM(NSUInteger, MGLCircleTranslationAnchor) {
circles on the map.
Use 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.
+ collection features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPointAnnotation`,
+ `MGLPointFeature`, `MGLPointCollection`, or `MGLPointCollectionFeature`
+ instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.
A 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,
@@ -83,12 +83,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: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +116,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.
@@ -150,52 +141,38 @@ MGL_EXPORT
/**
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`
+ 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<UIColor *> *circleColor;
+@property (nonatomic, null_resettable) NSExpression *circleColor;
#else
/**
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
`NSColor.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`
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *circleColor;
#endif
/**
@@ -208,27 +185,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 between 0 and 1 inclusive
+ * 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 +209,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 expression containing any of the following:
- You can set this property to an instance of:
+ * 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
- * `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 *> *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 no less than 0
+ * 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,74 +258,69 @@ 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`
+ 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<UIColor *> *circleStrokeColor;
+@property (nonatomic, null_resettable) NSExpression *circleStrokeColor;
#else
/**
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
`NSColor.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`
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *circleStrokeColor;
#endif
/**
@@ -369,27 +333,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 between 0 and 1 inclusive
+ * 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 +360,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 no less than 0
+ * 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 +387,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 +395,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:
+
+ * 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;
#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 +421,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:
- * `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;
#endif
/**
@@ -486,14 +442,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 +457,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/MGLCompassDirectionFormatter.m b/platform/darwin/src/MGLCompassDirectionFormatter.m
index 5f0cfae6f7..1ac6a82162 100644
--- a/platform/darwin/src/MGLCompassDirectionFormatter.m
+++ b/platform/darwin/src/MGLCompassDirectionFormatter.m
@@ -15,8 +15,8 @@
}
- (NSString *)stringFromDirection:(CLLocationDirection)direction {
- static NS_ARRAY_OF(NSString *) *shortStrings;
- static NS_ARRAY_OF(NSString *) *longStrings;
+ static NSArray<NSString *> *shortStrings;
+ static NSArray<NSString *> *longStrings;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shortStrings = @[
diff --git a/platform/darwin/src/MGLComputedShapeSource.h b/platform/darwin/src/MGLComputedShapeSource.h
new file mode 100644
index 0000000000..7e0037df89
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.h
@@ -0,0 +1,165 @@
+#import "MGLFoundation.h"
+#import "MGLGeometry.h"
+#import "MGLTypes.h"
+#import "MGLShapeSource.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol MGLFeature;
+
+/**
+ 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 used with the `MGLComputedShapeSource` class; it is ignored when
+ creating an `MGLShapeSource` object.
+ */
+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 used with the `MGLComputedShapeSource` class; it is ignored when
+ creating an `MGLShapeSource` object.
+ */
+extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates;
+
+/**
+ 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
+
+/**
+ `MGLComputedShapeSource` is a map content source that supplies vector shapes,
+ one tile at a time, to be shown on the map on demand. You implement a class
+ conforming to the `MGLComputedShapeSourceDataSource` protocol that returns
+ instances of `MGLShape` or `MGLFeature`, then add a computed shape source to an
+ `MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
+ layer defines the appearance of any content supplied by the computed shape
+ source.
+
+ `MGLComputedShapeSource` is similar to `MGLShapeSource` but is optimized for
+ data sets that change dynamically or are too large to fit completely in memory.
+ It is also useful for data that is divided into tiles in a format other than
+ <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tiles</a>. For
+ <a href="http://geojson.org/">GeoJSON</a> data, use the `MGLShapeSource` class.
+ For static tiles or Mapbox Vector Tiles, use the `MGLVectorTileSource` class.
+
+ You can add and remove sources dynamically using methods such as
+ `-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`. This class
+ cannot be represented in a style JSON file; you must add it ot the style at
+ runtime.
+ */
+MGL_EXPORT
+@interface MGLComputedShapeSource : MGLSource
+
+/**
+ 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>.
+
+ This class supports the following options:
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`,
+ `MGLShapeSourceOptionSimplificationTolerance`,
+ `MGLShapeSourceOptionWrapsCoordinates`, and
+ `MGLShapeSourceOptionClipsCoordinates`. Shapes provided by a computed
+ shape source cannot be clustered.
+
+ @param identifier A string that uniquely identifies the source.
+ @param options An `NSDictionary` of options for this source.
+ */
+- (instancetype)initWithIdentifier:(NSString *)identifier options:(nullable NSDictionary<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>.
+
+ This class supports the following options:
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`,
+ `MGLShapeSourceOptionSimplificationTolerance`,
+ `MGLShapeSourceOptionWrapsCoordinates`, and
+ `MGLShapeSourceOptionClipsCoordinates`. Shapes provided by a computed shape
+ source cannot be clustered.
+
+ @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 NSDictionary<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..04734d0ef5
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource.mm
@@ -0,0 +1,225 @@
+#import "MGLComputedShapeSource_Private.h"
+
+#import "MGLMapView_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLShape_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>
+
+const MGLShapeSourceOption MGLShapeSourceOptionWrapsCoordinates = @"MGLShapeSourceOptionWrapsCoordinates";
+const MGLShapeSourceOption MGLShapeSourceOptionClipsCoordinates = @"MGLShapeSourceOptionClipsCoordinates";
+
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary<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;
+}
+
+@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:(NSDictionary<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:(NSDictionary<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/MGLComputedShapeSource_Private.h b/platform/darwin/src/MGLComputedShapeSource_Private.h
new file mode 100644
index 0000000000..ec075e4bd7
--- /dev/null
+++ b/platform/darwin/src/MGLComputedShapeSource_Private.h
@@ -0,0 +1,12 @@
+#import "MGLFoundation.h"
+#import "MGLTypes.h"
+#import "MGLComputedShapeSource.h"
+
+#include <mbgl/style/sources/custom_geometry_source.hpp>
+
+NS_ASSUME_NONNULL_BEGIN
+
+MGL_EXPORT
+mbgl::style::CustomGeometrySource::Options MBGLCustomGeometrySourceOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options);
+
+NS_ASSUME_NONNULL_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.h b/platform/darwin/src/MGLFeature.h
index a13821cf96..62471abb16 100644
--- a/platform/darwin/src/MGLFeature.h
+++ b/platform/darwin/src/MGLFeature.h
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
/**
The `MGLFeature` protocol is used to provide details about geographic features
- contained in an `MGLShapeSource` or `MGLVectorSource` object. Each concrete
+ contained in an `MGLShapeSource` or `MGLVectorTileSource` object. Each concrete
subclass of `MGLShape` in turn has a subclass that conforms to this protocol. A
feature object associates a shape with an optional identifier and attributes.
@@ -23,8 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
In addition to adding data to the map, you can also extract data from the map:
`-[MGLMapView visibleFeaturesAtPoint:]` and related methods return feature
objects that correspond to features in the source. This enables you to inspect
- the properties of features in vector tiles loaded by `MGLVectorSource` objects.
- You also reuse these feature objects as overlay annotations.
+ the properties of features in vector tiles loaded by `MGLVectorTileSource`
+ objects. You also reuse these feature objects as overlay annotations.
While it is possible to add `MGLFeature`-conforming objects to the map as
annotations using `-[MGLMapView addAnnotations:]` and related methods, doing so
@@ -43,12 +43,12 @@ NS_ASSUME_NONNULL_BEGIN
source.
You can configure an `MGLVectorStyleLayer` object to include or exclude a
- specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ specific feature in an `MGLShapeSource` or `MGLVectorTileSource`. In the
`MGLVectorStyleLayer.predicate` property, compare the special `$id` attribute
to the feature’s identifier.
- In vector tiles loaded by `MGLVectorSource` objects, the identifier corresponds
- to the
+ In vector tiles loaded by `MGLVectorTileSource` objects, the identifier
+ corresponds to the
<a href="https://github.com/mapbox/vector-tile-spec/tree/master/2.1#42-features">feature identifier</a>
(`id`). If the source does not specify the feature’s identifier, the value of
this property is `nil`. If specified, the identifier may be an integer,
@@ -83,7 +83,7 @@ NS_ASSUME_NONNULL_BEGIN
A dictionary of attributes for this feature.
You can configure an `MGLVectorStyleLayer` object to include or exclude a
- specific feature in an `MGLShapeSource` or `MGLVectorSource`. In the
+ specific feature in an `MGLShapeSource` or `MGLVectorTileSource`. In the
`MGLVectorStyleLayer.predicate` property, compare a key of the attribute
dictionary to the value you want to include. For example, if you want an
`MGLLineStyleLayer` object to display only important features, you might assign
@@ -107,7 +107,7 @@ NS_ASSUME_NONNULL_BEGIN
`MGLSymbolStyleLayer.textField` to an `MGLStyleValue` object containing the
raw string value `{name}`.
- In vector tiles loaded by `MGLVectorSource` objects, the keys and values of
+ In vector tiles loaded by `MGLVectorTileSource` objects, the keys and values of
each feature’s attribute dictionary are determined by the source. Each
attribute name is a string, while each attribute value may be a null value,
Boolean value, integer, floating-point number, or string. These data types are
@@ -148,7 +148,7 @@ NS_ASSUME_NONNULL_BEGIN
when the feature instance is used to initialize an `MGLShapeSource` and that
source is added to the map and styled.
*/
-@property (nonatomic, copy) NS_DICTIONARY_OF(NSString *, id) *attributes;
+@property (nonatomic, copy) NSDictionary<NSString *, id> *attributes;
/**
Returns the feature attribute for the given attribute name.
@@ -167,7 +167,7 @@ NS_ASSUME_NONNULL_BEGIN
`attributes` property, and an `id` key corresponding to the receiver’s
`identifier` property.
*/
-- (NS_DICTIONARY_OF(NSString *, id) *)geoJSONDictionary;
+- (NSDictionary<NSString *, id> *)geoJSONDictionary;
@end
@@ -238,11 +238,12 @@ MGL_EXPORT
An `MGLShapeCollectionFeature` object associates a shape collection with an
optional identifier and attributes.
- `MGLShapeCollectionFeature` is most commonly used to add multiple shapes to a single
- `MGLShapeSource`. Configure the appearance of an `MGLSource`’s shape collection
- collectively using an `MGLSymbolStyleLayer` object, or use multiple instances of
- `MGLCircleStyleLayer`, `MGLFillStyleLayer`, and `MGLLineStyleLayer` to
- configure the appearance of each kind of shape inside the collection.
+ `MGLShapeCollectionFeature` is most commonly used to add multiple shapes to a
+ single `MGLShapeSource`. Configure the appearance of an `MGLSource`’s shape
+ collection collectively using an `MGLSymbolStyleLayer` object, or use multiple
+ instances of `MGLCircleStyleLayer`, `MGLFillStyleLayer`, and
+ `MGLLineStyleLayer` to configure the appearance of each kind of shape inside
+ the collection.
A shape collection feature is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.3">feature collection</a>
@@ -251,9 +252,9 @@ MGL_EXPORT
MGL_EXPORT
@interface MGLShapeCollectionFeature : MGLShapeCollection <MGLFeature>
-@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape<MGLFeature> *) *shapes;
+@property (nonatomic, copy, readonly) NSArray<MGLShape<MGLFeature> *> *shapes;
-+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes;
++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape<MGLFeature> *> *)shapes;
@end
diff --git a/platform/darwin/src/MGLFeature.mm b/platform/darwin/src/MGLFeature.mm
index 84f1a1ff25..02f67dca6e 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>
@@ -233,7 +233,7 @@ MGL_DEFINE_FEATURE_IS_EQUAL();
@dynamic shapes;
-+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)shapes {
++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape<MGLFeature> *> *)shapes {
return [super shapeCollectionWithShapes:shapes];
}
@@ -373,7 +373,7 @@ public:
}
};
-NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) {
+NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features) {
NSMutableArray *shapes = [NSMutableArray arrayWithCapacity:features.size()];
for (const auto &feature : features) {
[shapes addObject:MGLFeatureFromMBGLFeature(feature)];
@@ -414,7 +414,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at
return feature;
}
-NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) {
+NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier) {
NSMutableDictionary *feature = [@{@"type": @"Feature",
@"properties": (attributes) ?: [NSNull null],
@"geometry": geometry} mutableCopy];
diff --git a/platform/darwin/src/MGLFeature_Private.h b/platform/darwin/src/MGLFeature_Private.h
index 4137200b98..d4074b53ed 100644
--- a/platform/darwin/src/MGLFeature_Private.h
+++ b/platform/darwin/src/MGLFeature_Private.h
@@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN
vector tile features.
*/
MGL_EXPORT
-NS_ARRAY_OF(MGLShape <MGLFeature> *) *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features);
+NSArray<MGLShape <MGLFeature> *> *MGLFeaturesFromMBGLFeatures(const std::vector<mbgl::Feature> &features);
/**
Returns an `MGLFeature` object converted from the given mbgl::Feature
@@ -36,7 +36,7 @@ mbgl::Feature mbglFeature(mbgl::Feature feature, id identifier, NSDictionary *at
/**
Returns an `NSDictionary` representation of an `MGLFeature`.
*/
-NS_DICTIONARY_OF(NSString *, id) *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier);
+NSDictionary<NSString *, id> *NSDictionaryFeatureForGeometry(NSDictionary *geometry, NSDictionary *attributes, id identifier);
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLFillExtrusionStyleLayer.h b/platform/darwin/src/MGLFillExtrusionStyleLayer.h
index c4fb9aa77e..7c3a0773e4 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.
@@ -29,9 +29,10 @@ typedef NS_ENUM(NSUInteger, MGLFillExtrusionTranslationAnchor) {
extruded polygons on the map.
Use a fill-extrusion style layer to configure the visual appearance of polygon
- or multipolygon features in vector tiles loaded by an `MGLVectorSource` object
- or `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
- `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+ or multipolygon features. These features can come from vector tiles loaded by
+ an `MGLVectorTileSource` object, or they can be `MGLPolygon`,
+ `MGLPolygonFeature`, `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances
+ in an `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing fill-extrusion style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -44,8 +45,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 +77,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 no less than 0
+ * 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.
@@ -115,30 +108,23 @@ MGL_EXPORT
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`
+ 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<UIColor *> *fillExtrusionColor;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionColor;
#else
/**
The base color of this layer. The extrusion's surfaces will be shaded
@@ -146,30 +132,23 @@ MGL_EXPORT
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
`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 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 `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *fillExtrusionColor;
#endif
/**
@@ -184,27 +163,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 no less than 0
+ * 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 +188,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 between 0 and 1 inclusive
+ * 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 +216,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 +243,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 +251,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 +277,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 +298,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 +313,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..688ce4c1ac 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,22 @@ 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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillExtrusionPatternTransition:(MGLTransition )transition {
@@ -252,21 +253,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 +288,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..90740223eb 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.
@@ -29,9 +28,10 @@ typedef NS_ENUM(NSUInteger, MGLFillTranslationAnchor) {
optionally stroked) polygons on the map.
Use a fill style layer to configure the visual appearance of polygon or
- multipolygon features in vector tiles loaded by an `MGLVectorSource` object or
- `MGLPolygon`, `MGLPolygonFeature`, `MGLMultiPolygon`, or
- `MGLMultiPolygonFeature` instances in an `MGLShapeSource` object.
+ multipolygon features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPolygon`, `MGLPolygonFeature`,
+ `MGLMultiPolygon`, or `MGLMultiPolygonFeature` instances in an `MGLShapeSource`
+ or `MGLComputedShapeSource` object.
You can access an existing fill style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -44,7 +44,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,80 +72,71 @@ 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`
+ 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<UIColor *> *fillColor;
+@property (nonatomic, null_resettable) NSExpression *fillColor;
#else
/**
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
`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 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 `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *fillColor;
#endif
/**
@@ -159,27 +150,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 between 0 and 1 inclusive
+ * 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.
@@ -193,51 +176,37 @@ MGL_EXPORT
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`
+ and `fillAntialiased` is set to an expression that evaluates to `YES`.
+ Otherwise, it is ignored.
+
+ 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<UIColor *> *fillOutlineColor;
+@property (nonatomic, null_resettable) NSExpression *fillOutlineColor;
#else
/**
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`
+ and `fillAntialiased` is set to an expression that evaluates to `YES`.
+ Otherwise, it is ignored.
+
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *fillOutlineColor;
#endif
/**
@@ -249,15 +218,22 @@ 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).
+ image width and height must be a factor of two (2, 4, 8, ..., 512). Note that
+ zoom-dependent expressions will be evaluated only at integer zoom levels.
+
+ You can set this property to an expression containing any of the following:
- You can set this property to an instance of:
+ * 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 +248,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 +256,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 +282,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 +303,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 +318,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..c975e28d6b 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,22 @@ 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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setFillPatternTransition:(MGLTransition )transition {
@@ -241,21 +242,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 +277,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/MGLGeometry.h b/platform/darwin/src/MGLGeometry.h
index 7c68033abf..a8d3759106 100644
--- a/platform/darwin/src/MGLGeometry.h
+++ b/platform/darwin/src/MGLGeometry.h
@@ -14,6 +14,24 @@ typedef struct __attribute__((objc_boxable)) MGLCoordinateSpan {
CLLocationDegrees longitudeDelta;
} MGLCoordinateSpan;
+/* Defines a point on the map in Mercator projection for a specific zoom level. */
+typedef struct __attribute__((objc_boxable)) MGLMapPoint {
+ /** X coordinate representing a longitude in Mercator projection. */
+ CGFloat x;
+ /** Y coordinate representing a latitide in Mercator projection. */
+ CGFloat y;
+ /** Zoom level at which the X and Y coordinates are valid. */
+ CGFloat zoomLevel;
+} MGLMapPoint;
+
+/* Defines a 4x4 matrix. */
+typedef struct MGLMatrix4 {
+ double m00, m01, m02, m03;
+ double m10, m11, m12, m13;
+ double m20, m21, m22, m23;
+ double m30, m31, m32, m33;
+} MGLMatrix4;
+
/**
Creates a new `MGLCoordinateSpan` from the given latitudinal and longitudinal
deltas.
@@ -26,6 +44,17 @@ NS_INLINE MGLCoordinateSpan MGLCoordinateSpanMake(CLLocationDegrees latitudeDelt
}
/**
+ Creates a new `MGLMapPoint` from the given X and Y coordinates, and zoom level.
+ */
+NS_INLINE MGLMapPoint MGLMapPointMake(CGFloat x, CGFloat y, CGFloat zoomLevel) {
+ MGLMapPoint point;
+ point.x = x;
+ point.y = y;
+ point.zoomLevel = zoomLevel;
+ return point;
+}
+
+/**
Returns `YES` if the two coordinate spans represent the same latitudinal change
and the same longitudinal change.
*/
@@ -181,4 +210,7 @@ NS_INLINE CLLocationDegrees MGLDegreesFromRadians(CGFloat radians) {
return radians * 180 / M_PI;
}
+/** Returns Mercator projection of a WGS84 coordinate at the specified zoom level. */
+extern MGL_EXPORT MGLMapPoint MGLMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel);
+
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLGeometry.mm b/platform/darwin/src/MGLGeometry.mm
index 715a70f0b8..6785779570 100644
--- a/platform/darwin/src/MGLGeometry.mm
+++ b/platform/darwin/src/MGLGeometry.mm
@@ -106,3 +106,19 @@ CGPoint MGLPointRounded(CGPoint point) {
#endif
return CGPointMake(round(point.x * scaleFactor) / scaleFactor, round(point.y * scaleFactor) / scaleFactor);
}
+
+MGLMapPoint MGLMapPointForCoordinate(CLLocationCoordinate2D coordinate, double zoomLevel) {
+ mbgl::Point<double> projectedCoordinate = mbgl::Projection::project(MGLLatLngFromLocationCoordinate2D(coordinate), std::pow(2.0, zoomLevel));
+ return MGLMapPointMake(projectedCoordinate.x, projectedCoordinate.y, zoomLevel);
+}
+
+MGLMatrix4 MGLMatrix4Make(std::array<double, 16> array) {
+ MGLMatrix4 mat4 = {
+ .m00 = array[0], .m01 = array[1], .m02 = array[2], .m03 = array[3],
+ .m10 = array[4], .m11 = array[5], .m12 = array[6], .m13 = array[7],
+ .m20 = array[8], .m21 = array[9], .m22 = array[10], .m23 = array[11],
+ .m30 = array[12], .m31 = array[13], .m32 = array[14], .m33 = array[15]
+ };
+ return mat4;
+}
+
diff --git a/platform/darwin/src/MGLGeometry_Private.h b/platform/darwin/src/MGLGeometry_Private.h
index 8b9c6c2327..0bff9b09f5 100644
--- a/platform/darwin/src/MGLGeometry_Private.h
+++ b/platform/darwin/src/MGLGeometry_Private.h
@@ -128,3 +128,5 @@ MGLRadianCoordinate2D MGLRadianCoordinateAtDistanceFacingDirection(MGLRadianCoor
CLLocationDirection MGLDirectionBetweenCoordinates(CLLocationCoordinate2D firstCoordinate, CLLocationCoordinate2D secondCoordinate);
CGPoint MGLPointRounded(CGPoint point);
+
+MGLMatrix4 MGLMatrix4Make(std::array<double, 16> mat);
diff --git a/platform/darwin/src/MGLHeatmapStyleLayer.h b/platform/darwin/src/MGLHeatmapStyleLayer.h
new file mode 100644
index 0000000000..ad7ba5de01
--- /dev/null
+++ b/platform/darwin/src/MGLHeatmapStyleLayer.h
@@ -0,0 +1,215 @@
+// 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. These
+ features can come from vector tiles loaded by an `MGLVectorTileSource` object,
+ or they can be `MGLPointAnnotation`, `MGLPointFeature`, `MGLPointCollection`,
+ or `MGLPointCollectionFeature` instances in an `MGLShapeSource` or
+ `MGLComputedShapeSource` 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: "mgl_interpolate:withCurveType:parameters:stops:(magnitude, 'linear', nil, %@)",
+ [0: 0,
+ 6: 1])
+ layer.heatmapIntensity = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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
+
+#if TARGET_OS_IPHONE
+/**
+ The color of each screen point based on its density value in a heatmap. This
+ property is normally set to an interpolation or step expression with the
+ `$heatmapDensity` value as its 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;
+#else
+/**
+ The color of each screen point based on its density value in a heatmap. This
+ property is normally set to an interpolation or step expression with the
+ `$heatmapDensity` value as its 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 `NSColor` 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;
+#endif
+
+/**
+ 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 no less than 0
+ * 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 between 0 and 1 inclusive
+ * 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 no less than 1
+ * 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 no less than 0
+ * 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..224680160a
--- /dev/null
+++ b/platform/darwin/src/MGLHillshadeStyleLayer.h
@@ -0,0 +1,324 @@
+// 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 `MGLVectorTileSource` 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
+
+#if TARGET_OS_IPHONE
+/**
+ 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;
+#else
+/**
+ 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
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
+
+/**
+ 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 between 0 and 1 inclusive
+ * 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;
+
+#if TARGET_OS_IPHONE
+/**
+ 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;
+#else
+/**
+ The shading color of areas that faces towards the light source.
+
+ The default value of this property is an expression that evaluates to
+ `NSColor.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 `NSColor` 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;
+#endif
+
+/**
+ 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 between 0 and 359 inclusive
+ * 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;
+
+#if TARGET_OS_IPHONE
+/**
+ 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;
+#else
+/**
+ The shading color of areas that face away from the light source.
+
+ The default value of this property is an expression that evaluates to
+ `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:
+
+ * Constant `NSColor` 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;
+#endif
+
+/**
+ 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..cf4aa50112 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.
@@ -117,40 +132,48 @@ MGL_EXPORT
/**
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:
+ 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
- * `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<UIColor *> *color;
+@property (nonatomic) NSExpression *color;
#else
/**
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
`NSColor.whiteColor`.
- 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 `NSColor` 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.
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;
+@property (nonatomic) NSExpression *color;
#endif
/**
@@ -164,21 +187,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 between 0 and 1 inclusive
+ * 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..32c8ece6c5 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.
@@ -79,9 +78,10 @@ typedef NS_ENUM(NSUInteger, MGLLineTranslationAnchor) {
polylines on the map.
Use a line style layer to configure the visual appearance of polyline or
- multipolyline features in vector tiles loaded by an `MGLVectorSource` object or
- `MGLPolyline`, `MGLPolylineFeature`, `MGLMultiPolyline`, or
- `MGLMultiPolylineFeature` instances in an `MGLShapeSource` object.
+ multipolyline features. These features can come from vector tiles loaded by an
+ `MGLVectorTileSource` object, or they can be `MGLPolyline`,
+ `MGLPolylineFeature`, `MGLMultiPolyline`, or `MGLMultiPolylineFeature`
+ instances in an `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing line style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -94,12 +94,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: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +126,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 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 *> *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 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 *> *lineRoundLimit;
+@property (nonatomic, null_resettable) NSExpression *lineRoundLimit;
#pragma mark - Accessing the Paint Attributes
@@ -211,27 +227,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 no less than 0
+ * 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.
@@ -244,58 +252,44 @@ MGL_EXPORT
/**
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`
+ 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<UIColor *> *lineColor;
+@property (nonatomic, null_resettable) NSExpression *lineColor;
#else
/**
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
`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 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 `NSColor` 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;
+@property (nonatomic, null_resettable) NSExpression *lineColor;
#endif
/**
@@ -308,7 +302,10 @@ MGL_EXPORT
/**
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 points, multiply the length by the current line width.
+ length to points, multiply the length by the current line width. Note that
+ GeoJSON sources with `lineMetrics: true` specified won't render dashed lines to
+ the expected scale. Also note that zoom-dependent expressions will be evaluated
+ only at integer zoom levels.
This property is measured in line widths.
@@ -319,13 +316,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:
+
+ * Constant array values no less than 0
+ * 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<NSArray<NSNumber *> *> *lineDashPattern;
+@property (nonatomic, null_resettable) NSExpression *lineDashPattern;
/**
The transition affecting any changes to this layer’s `lineDashPattern` property.
@@ -334,7 +337,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 +345,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 no less than 0
+ * 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 +374,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 +398,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 between 0 and 1 inclusive
+ * 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 +423,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 +450,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 +458,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 +484,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 +505,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 +520,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:
+
+ * 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
- * `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 *> *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 no less than 0
+ * 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..619cc70afe 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,22 @@ 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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
- (void)setLinePatternTransition:(MGLTransition )transition {
@@ -411,21 +412,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 +447,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.h b/platform/darwin/src/MGLMapSnapshotter.h
index 426ab1bb00..976213c8ba 100644
--- a/platform/darwin/src/MGLMapSnapshotter.h
+++ b/platform/darwin/src/MGLMapSnapshotter.h
@@ -140,7 +140,7 @@ typedef void (^MGLMapSnapshotCompletionHandler)(MGLMapSnapshot* _Nullable snapsh
```swift
let camera = MGLMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365), fromDistance: 100, pitch: 20, heading: 0)
- let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL(), camera: camera, size: CGSize(width: 320, height: 480))
+ let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL, camera: camera, size: CGSize(width: 320, height: 480))
options.zoomLevel = 10
let snapshotter = MGLMapSnapshotter(options: options)
diff --git a/platform/darwin/src/MGLMapSnapshotter.mm b/platform/darwin/src/MGLMapSnapshotter.mm
index db236a8aeb..19fa0223a4 100644
--- a/platform/darwin/src/MGLMapSnapshotter.mm
+++ b/platform/darwin/src/MGLMapSnapshotter.mm
@@ -89,7 +89,7 @@ const CGFloat MGLSnapshotterMinimumPixelSize = 64;
std::shared_ptr<mbgl::ThreadPool> _mbglThreadPool;
std::unique_ptr<mbgl::MapSnapshotter> _mbglMapSnapshotter;
std::unique_ptr<mbgl::Actor<mbgl::MapSnapshotter::Callback>> _snapshotCallback;
- NS_ARRAY_OF(MGLAttributionInfo *) *_attributionInfo;
+ NSArray<MGLAttributionInfo *> *_attributionInfo;
}
@@ -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/MGLMultiPoint.mm b/platform/darwin/src/MGLMultiPoint.mm
index 240dad9614..5a7262b0a4 100644
--- a/platform/darwin/src/MGLMultiPoint.mm
+++ b/platform/darwin/src/MGLMultiPoint.mm
@@ -70,7 +70,7 @@
return _coordinates.size();
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPointCount
++ (NSSet<NSString *> *)keyPathsForValuesAffectingPointCount
{
return [NSSet setWithObjects:@"coordinates", nil];
}
diff --git a/platform/darwin/src/MGLNetworkConfiguration.h b/platform/darwin/src/MGLNetworkConfiguration.h
index f1fe7bab2c..2db46d78c5 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.h
+++ b/platform/darwin/src/MGLNetworkConfiguration.h
@@ -2,12 +2,6 @@
NS_ASSUME_NONNULL_BEGIN
-/// The default base URL for Mapbox APIs other than the telemetry API.
-extern NSString * const MGLDefaultMapboxAPIBaseURL;
-
-/// The PRC base URL for Mapbox APIs other than the telemetry API.
-extern NSString * const MGLChinaMapboxAPIBaseURL;
-
/**
The MGLNetworkConfiguration object provides a global way to set a base API URL for
retrieval of map data, styles, and other resources.
@@ -19,7 +13,7 @@ extern NSString * const MGLChinaMapboxAPIBaseURL;
@interface MGLNetworkConfiguration : NSObject
/// Returns the shared instance of the `MGLNetworkConfiguration` class.
-+ (instancetype)sharedManager;
+@property (class, nonatomic, readonly) MGLNetworkConfiguration *sharedManager;
/// The current API base URL. If `nil`, the Mapbox default base API URL is in use.
@property (atomic, nullable) NSURL *apiBaseURL;
diff --git a/platform/darwin/src/MGLNetworkConfiguration.m b/platform/darwin/src/MGLNetworkConfiguration.m
index d0ee01c5a2..4cfa405a1a 100644
--- a/platform/darwin/src/MGLNetworkConfiguration.m
+++ b/platform/darwin/src/MGLNetworkConfiguration.m
@@ -1,8 +1,5 @@
#import "MGLNetworkConfiguration.h"
-NSString * const MGLDefaultMapboxAPIBaseURL = @"https://api.mapbox.com";
-NSString * const MGLChinaMapboxAPIBaseURL = @"https://api.mapbox.cn";
-
@implementation MGLNetworkConfiguration
+ (void)load {
@@ -16,7 +13,7 @@ NSString * const MGLChinaMapboxAPIBaseURL = @"https://api.mapbox.cn";
+ (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/MGLOfflineStorage.h b/platform/darwin/src/MGLOfflineStorage.h
index b009f893b3..ab36592634 100644
--- a/platform/darwin/src/MGLOfflineStorage.h
+++ b/platform/darwin/src/MGLOfflineStorage.h
@@ -70,7 +70,7 @@ typedef NSString *MGLOfflinePackUserInfoKey NS_EXTENSIBLE_STRING_ENUM;
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState;
-extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyState")));
+extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyState")));
/**
The key for an `NSValue` object that indicates an offline pack’s current
@@ -81,7 +81,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackStateUserInfoKey __attribute__(
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress;
-extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyProgress")));
+extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyProgress")));
/**
The key for an `NSError` object that is encountered in the course of
@@ -91,7 +91,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackProgressUserInfoKey __attribute
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError;
-extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyError")));
+extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyError")));
/**
The key for an `NSNumber` object that indicates the maximum number of
@@ -103,7 +103,7 @@ extern MGL_EXPORT NSString * const MGLOfflinePackErrorUserInfoKey __attribute__(
*/
extern MGL_EXPORT const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount;
-extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((deprecated("Use MGLOfflinePackUserInfoKeyMaximumCount")));
+extern MGL_EXPORT NSString * const MGLOfflinePackMaximumCountUserInfoKey __attribute__((unavailable("Use MGLOfflinePackUserInfoKeyMaximumCount")));
/**
A block to be called once an offline pack has been completely created and
@@ -170,7 +170,7 @@ MGL_EXPORT
/**
Returns the shared offline storage object.
*/
-+ (instancetype)sharedOfflineStorage;
+@property (class, nonatomic, readonly) MGLOfflineStorage *sharedOfflineStorage;
#pragma mark - Accessing the Delegate
@@ -199,7 +199,7 @@ MGL_EXPORT
`packs` property, observe KVO change notifications on the `packs` key path.
The initial load results in an `NSKeyValueChangeSetting` change.
*/
-@property (nonatomic, strong, readonly, nullable) NS_ARRAY_OF(MGLOfflinePack *) *packs;
+@property (nonatomic, strong, readonly, nullable) NSArray<MGLOfflinePack *> *packs;
/**
Creates and registers an offline pack that downloads the resources needed to
diff --git a/platform/darwin/src/MGLOfflineStorage.mm b/platform/darwin/src/MGLOfflineStorage.mm
index 7085aa58e5..f4e454534d 100644
--- a/platform/darwin/src/MGLOfflineStorage.mm
+++ b/platform/darwin/src/MGLOfflineStorage.mm
@@ -26,17 +26,13 @@ const NSNotificationName MGLOfflinePackErrorNotification = @"MGLOfflinePackError
const NSNotificationName MGLOfflinePackMaximumMapboxTilesReachedNotification = @"MGLOfflinePackMaximumMapboxTilesReached";
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyState = @"State";
-NSString * const MGLOfflinePackStateUserInfoKey = MGLOfflinePackUserInfoKeyState;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyProgress = @"Progress";
-NSString * const MGLOfflinePackProgressUserInfoKey = MGLOfflinePackUserInfoKeyProgress;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyError = @"Error";
-NSString * const MGLOfflinePackErrorUserInfoKey = MGLOfflinePackUserInfoKeyError;
const MGLOfflinePackUserInfoKey MGLOfflinePackUserInfoKeyMaximumCount = @"MaximumCount";
-NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoKeyMaximumCount;
@interface MGLOfflineStorage ()
-@property (nonatomic, strong, readwrite) NS_MUTABLE_ARRAY_OF(MGLOfflinePack *) *packs;
+@property (nonatomic, strong, readwrite) NSMutableArray<MGLOfflinePack *> *packs;
@property (nonatomic) mbgl::DefaultFileSource *mbglFileSource;
@property (nonatomic, getter=isPaused) BOOL paused;
@@ -247,7 +243,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
_mbglFileSource = nullptr;
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context {
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
// Synchronize the file source’s access token with the global one in MGLAccountManager.
if ([keyPath isEqualToString:@"accessToken"] && object == [MGLAccountManager sharedManager]) {
NSString *accessToken = change[NSKeyValueChangeNewKey];
@@ -340,7 +336,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
}
- (void)reloadPacks {
- [self getPacksWithCompletionHandler:^(NS_ARRAY_OF(MGLOfflinePack *) *packs, __unused NSError * _Nullable error) {
+ [self getPacksWithCompletionHandler:^(NSArray<MGLOfflinePack *> *packs, __unused NSError * _Nullable error) {
for (MGLOfflinePack *pack in self.packs) {
[pack invalidate];
}
@@ -348,7 +344,7 @@ NSString * const MGLOfflinePackMaximumCountUserInfoKey = MGLOfflinePackUserInfoK
}];
}
-- (void)getPacksWithCompletionHandler:(void (^)(NS_ARRAY_OF(MGLOfflinePack *) *packs, NSError * _Nullable error))completion {
+- (void)getPacksWithCompletionHandler:(void (^)(NSArray<MGLOfflinePack *> *packs, NSError * _Nullable error))completion {
self.mbglFileSource->listOfflineRegions([&, completion](std::exception_ptr exception, mbgl::optional<std::vector<mbgl::OfflineRegion>> regions) {
NSError *error;
if (exception) {
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.h b/platform/darwin/src/MGLOpenGLStyleLayer.h
index 0b494e8062..df8d2c5365 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.h
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.h
@@ -1,9 +1,11 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
+#import <QuartzCore/QuartzCore.h>
#import "MGLFoundation.h"
#import "MGLStyleValue.h"
#import "MGLStyleLayer.h"
+#import "MGLGeometry.h"
NS_ASSUME_NONNULL_BEGIN
@@ -17,6 +19,7 @@ typedef struct MGLStyleLayerDrawingContext {
CLLocationDirection direction;
CGFloat pitch;
CGFloat fieldOfView;
+ MGLMatrix4 projectionMatrix;
} MGLStyleLayerDrawingContext;
MGL_EXPORT
diff --git a/platform/darwin/src/MGLOpenGLStyleLayer.mm b/platform/darwin/src/MGLOpenGLStyleLayer.mm
index 8933a77382..d89fbc80c3 100644
--- a/platform/darwin/src/MGLOpenGLStyleLayer.mm
+++ b/platform/darwin/src/MGLOpenGLStyleLayer.mm
@@ -3,53 +3,53 @@
#import "MGLMapView_Private.h"
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
+#import "MGLGeometry_Private.h"
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/math/wrap.hpp>
-/**
- Runs the preparation handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+class MGLOpenGLLayerHost : public mbgl::style::CustomLayerHost {
+public:
+ MGLOpenGLLayerHost(MGLOpenGLStyleLayer *styleLayer) {
+ layerRef = styleLayer;
+ layer = nil;
+ }
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLPrepareCustomStyleLayer(void *context) {
- MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- [layer didMoveToMapView:layer.style.mapView];
-}
+ void initialize() {
+ if (layerRef == nil) return;
+ else if (layer == nil) layer = layerRef;
-/**
- Runs the drawing handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+ [layer didMoveToMapView:layer.style.mapView];
+ }
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLDrawCustomStyleLayer(void *context, const mbgl::style::CustomLayerRenderParameters &params) {
- MGLOpenGLStyleLayer *layer = (__bridge MGLOpenGLStyleLayer *)context;
- MGLStyleLayerDrawingContext drawingContext = {
- .size = CGSizeMake(params.width, params.height),
- .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
- .zoomLevel = params.zoom,
- .direction = mbgl::util::wrap(params.bearing, 0., 360.),
- .pitch = static_cast<CGFloat>(params.pitch),
- .fieldOfView = static_cast<CGFloat>(params.fieldOfView),
- };
- [layer drawInMapView:layer.style.mapView withContext:drawingContext];
-}
+ void render(const mbgl::style::CustomLayerRenderParameters &params) {
+ if(!layer) return;
+
+ MGLStyleLayerDrawingContext drawingContext = {
+ .size = CGSizeMake(params.width, params.height),
+ .centerCoordinate = CLLocationCoordinate2DMake(params.latitude, params.longitude),
+ .zoomLevel = params.zoom,
+ .direction = mbgl::util::wrap(params.bearing, 0., 360.),
+ .pitch = static_cast<CGFloat>(params.pitch),
+ .fieldOfView = static_cast<CGFloat>(params.fieldOfView),
+ .projectionMatrix = MGLMatrix4Make(params.projectionMatrix)
+ };
+ [layer drawInMapView:layer.style.mapView withContext:drawingContext];
+ }
-/**
- Runs the completion handler block contained in the given context, which is
- implicitly an instance of `MGLOpenGLStyleLayer`.
+ void contextLost() {}
- @param context An `MGLOpenGLStyleLayer` instance that was provided as context
- when creating an OpenGL style layer.
- */
-void MGLFinishCustomStyleLayer(void *context) {
- MGLOpenGLStyleLayer *layer = (__bridge_transfer MGLOpenGLStyleLayer *)context;
- [layer willMoveFromMapView:layer.style.mapView];
-}
+ void deinitialize() {
+ if (layer == nil) return;
+
+ [layer willMoveFromMapView:layer.style.mapView];
+ layerRef = layer;
+ layer = nil;
+ }
+private:
+ __weak MGLOpenGLStyleLayer * layerRef;
+ MGLOpenGLStyleLayer * layer = nil;
+};
/**
An `MGLOpenGLStyleLayer` is a style layer that is rendered by OpenGL code that
@@ -98,10 +98,7 @@ void MGLFinishCustomStyleLayer(void *context) {
*/
- (instancetype)initWithIdentifier:(NSString *)identifier {
auto layer = std::make_unique<mbgl::style::CustomLayer>(identifier.UTF8String,
- MGLPrepareCustomStyleLayer,
- MGLDrawCustomStyleLayer,
- MGLFinishCustomStyleLayer,
- (__bridge_retained void *)self);
+ std::make_unique<MGLOpenGLLayerHost>(self));
return self = [super initWithPendingLayer:std::move(layer)];
}
@@ -110,22 +107,15 @@ void MGLFinishCustomStyleLayer(void *context) {
}
#pragma mark - Adding to and removing from a map view
-
-- (void)setStyle:(MGLStyle *)style {
- if (_style && style) {
- [NSException raise:@"MGLLayerReuseException"
- format:@"%@ cannot be added to more than one MGLStyle at a time.", self];
- }
- _style = style;
-}
-
- (void)addToStyle:(MGLStyle *)style belowLayer:(MGLStyleLayer *)otherLayer {
self.style = style;
+ self.style.openGLLayers[self.identifier] = self;
[super addToStyle:style belowLayer:otherLayer];
}
- (void)removeFromStyle:(MGLStyle *)style {
[super removeFromStyle:style];
+ self.style.openGLLayers[self.identifier] = nil;
self.style = nil;
}
diff --git a/platform/darwin/src/MGLPointAnnotation.h b/platform/darwin/src/MGLPointAnnotation.h
index 1ef0962f99..3dac7a969c 100644
--- a/platform/darwin/src/MGLPointAnnotation.h
+++ b/platform/darwin/src/MGLPointAnnotation.h
@@ -15,7 +15,7 @@ NS_ASSUME_NONNULL_BEGIN
You can add point shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s point shapes collectively using an `MGLCircleStyleLayer` or
+ `MGLVectorTileSource`’s point shapes collectively using an `MGLCircleStyleLayer` or
`MGLSymbolStyleLayer` object.
For more interactivity, add a selectable point annotation to a map view using
@@ -28,7 +28,8 @@ NS_ASSUME_NONNULL_BEGIN
default content of the annotation’s callout (on iOS) or popover (on macOS).
To group multiple related points together in one shape, use an
- `MGLPointCollection` or `MGLShapeCollection` object.
+ `MGLPointCollection` or `MGLShapeCollection` object. To access
+ a point’s attributes, use an `MGLPointFeature` object.
A point shape is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.2">Point</a> geometry
diff --git a/platform/darwin/src/MGLPointCollection.h b/platform/darwin/src/MGLPointCollection.h
index 74b30385a0..65ce95cb0f 100644
--- a/platform/darwin/src/MGLPointCollection.h
+++ b/platform/darwin/src/MGLPointCollection.h
@@ -14,8 +14,9 @@
You can add point collections to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s point collections collectively using an
- `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s point collections collectively using an
+ `MGLCircleStyleLayer` or `MGLSymbolStyleLayer` object. To access a point
+ collection’s attributes, use an `MGLPointCollectionFeature` object.
You cannot add an `MGLPointCollection` object directly to a map view as an
annotation. However, you can create individual `MGLPointAnnotation` objects
diff --git a/platform/darwin/src/MGLPolygon+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.h b/platform/darwin/src/MGLPolygon.h
index 3fcc1be76d..810a8b78ae 100644
--- a/platform/darwin/src/MGLPolygon.h
+++ b/platform/darwin/src/MGLPolygon.h
@@ -17,8 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
You can add polygon shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s polygons collectively using an `MGLFillStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s polygons collectively using an `MGLFillStyleLayer` or
+ `MGLSymbolStyleLayer` object. To access a polygon’s attributes, use an
+ `MGLPolygonFeature` object.
Alternatively, you can add a polygon overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
@@ -56,7 +57,7 @@ MGL_EXPORT
If there are no interior polygons, the value of this property is `nil`.
*/
-@property (nonatomic, nullable, readonly) NS_ARRAY_OF(MGLPolygon *) *interiorPolygons;
+@property (nonatomic, nullable, readonly) NSArray<MGLPolygon *> *interiorPolygons;
/**
Creates and returns an `MGLPolygon` object from the specified set of
@@ -81,7 +82,7 @@ MGL_EXPORT
is considered to have no interior polygons.
@return A new polygon object.
*/
-+ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NS_ARRAY_OF(MGLPolygon *) *)interiorPolygons;
++ (instancetype)polygonWithCoordinates:(const CLLocationCoordinate2D *)coords count:(NSUInteger)count interiorPolygons:(nullable NSArray<MGLPolygon *> *)interiorPolygons;
@end
@@ -95,8 +96,8 @@ MGL_EXPORT
You can add multipolygon shapes to the map by adding them to an
`MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s multipolygons collectively using an `MGLFillStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s multipolygons collectively using an `MGLFillStyleLayer`
+ or `MGLSymbolStyleLayer` object.
You cannot add an `MGLMultiPolygon` object directly to a map view using
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
@@ -108,7 +109,7 @@ MGL_EXPORT
/**
An array of polygons forming the multipolygon.
*/
-@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolygon *) *polygons;
+@property (nonatomic, copy, readonly) NSArray<MGLPolygon *> *polygons;
/**
Creates and returns a multipolygon object consisting of the given polygons.
@@ -116,7 +117,7 @@ MGL_EXPORT
@param polygons The array of polygons defining the shape.
@return A new multipolygon object.
*/
-+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons;
++ (instancetype)multiPolygonWithPolygons:(NSArray<MGLPolygon *> *)polygons;
@end
diff --git a/platform/darwin/src/MGLPolygon.mm b/platform/darwin/src/MGLPolygon.mm
index e7843224e9..b80504707b 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,11 +102,33 @@
@"coordinates": self.mgl_coordinates};
}
+- (NSArray<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 ()
-@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolygon *) *polygons;
+@property (nonatomic, copy, readwrite) NSArray<MGLPolygon *> *polygons;
@end
@@ -116,11 +138,11 @@
@synthesize overlayBounds = _overlayBounds;
-+ (instancetype)multiPolygonWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons {
++ (instancetype)multiPolygonWithPolygons:(NSArray<MGLPolygon *> *)polygons {
return [[self alloc] initWithPolygons:polygons];
}
-- (instancetype)initWithPolygons:(NS_ARRAY_OF(MGLPolygon *) *)polygons {
+- (instancetype)initWithPolygons:(NSArray<MGLPolygon *> *)polygons {
if (self = [super init]) {
_polygons = polygons;
diff --git a/platform/darwin/src/MGLPolygon_Private.h b/platform/darwin/src/MGLPolygon_Private.h
new file mode 100644
index 0000000000..b006f2d77f
--- /dev/null
+++ b/platform/darwin/src/MGLPolygon_Private.h
@@ -0,0 +1,11 @@
+#import "MGLPolygon.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolygon (Private)
+
+- (NSArray<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.h b/platform/darwin/src/MGLPolyline.h
index e46baa91cc..8e9007686b 100644
--- a/platform/darwin/src/MGLPolyline.h
+++ b/platform/darwin/src/MGLPolyline.h
@@ -17,8 +17,9 @@ NS_ASSUME_NONNULL_BEGIN
You can add polyline shapes to the map by adding them to an `MGLShapeSource`
object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s polylines collectively using an `MGLLineStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s polylines collectively using an `MGLLineStyleLayer` or
+ `MGLSymbolStyleLayer` object. To access a polyline’s attributes, use an
+ `MGLPolylineFeature` object.
Alternatively, you can add a polyline overlay directly to a map view using the
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]` method. Configure
@@ -74,8 +75,8 @@ MGL_EXPORT
You can add multipolyline shapes to the map by adding them to an
`MGLShapeSource` object. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s multipolylines collectively using an `MGLLineStyleLayer` or
- `MGLSymbolStyleLayer` object.
+ `MGLVectorTileSource`’s multipolylines collectively using an
+ `MGLLineStyleLayer` or `MGLSymbolStyleLayer` object.
You cannot add an `MGLMultiPolyline` object directly to a map view using
`-[MGLMapView addAnnotation:]` or `-[MGLMapView addOverlay:]`. However, you can
@@ -91,7 +92,7 @@ MGL_EXPORT
/**
An array of polygons forming the multipolyline.
*/
-@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLPolyline *) *polylines;
+@property (nonatomic, copy, readonly) NSArray<MGLPolyline *> *polylines;
/**
Creates and returns a multipolyline object consisting of the given polylines.
@@ -99,7 +100,7 @@ MGL_EXPORT
@param polylines The array of polylines defining the shape.
@return A new multipolyline object.
*/
-+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines;
++ (instancetype)multiPolylineWithPolylines:(NSArray<MGLPolyline *> *)polylines;
@end
diff --git a/platform/darwin/src/MGLPolyline.mm b/platform/darwin/src/MGLPolyline.mm
index 0e371a4dda..a028db8176 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};
}
+- (NSArray<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]);
}
@@ -63,9 +72,12 @@
if (count > 1 || middle > traveled) {
for (NSUInteger i = 0; i < count; i++) {
-
+
+ // Avoid a heap buffer overflow when there are only two coordinates.
+ NSUInteger nextIndex = (i + 1 == count) ? 0 : 1;
+
MGLRadianCoordinate2D from = MGLRadianCoordinateFromLocationCoordinate(coordinates[i]);
- MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + 1]);
+ MGLRadianCoordinate2D to = MGLRadianCoordinateFromLocationCoordinate(coordinates[i + nextIndex]);
if (traveled >= middle) {
double overshoot = middle - traveled;
@@ -82,7 +94,6 @@
}
traveled += (MGLDistanceBetweenRadianCoordinates(from, to) * mbgl::util::EARTH_RADIUS_M);
-
}
}
@@ -112,7 +123,7 @@
@interface MGLMultiPolyline ()
-@property (nonatomic, copy, readwrite) NS_ARRAY_OF(MGLPolyline *) *polylines;
+@property (nonatomic, copy, readwrite) NSArray<MGLPolyline *> *polylines;
@end
@@ -122,11 +133,11 @@
@synthesize overlayBounds = _overlayBounds;
-+ (instancetype)multiPolylineWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines {
++ (instancetype)multiPolylineWithPolylines:(NSArray<MGLPolyline *> *)polylines {
return [[self alloc] initWithPolylines:polylines];
}
-- (instancetype)initWithPolylines:(NS_ARRAY_OF(MGLPolyline *) *)polylines {
+- (instancetype)initWithPolylines:(NSArray<MGLPolyline *> *)polylines {
if (self = [super init]) {
_polylines = polylines;
diff --git a/platform/darwin/src/MGLPolyline_Private.h b/platform/darwin/src/MGLPolyline_Private.h
new file mode 100644
index 0000000000..ff4fabaa78
--- /dev/null
+++ b/platform/darwin/src/MGLPolyline_Private.h
@@ -0,0 +1,12 @@
+#import "MGLPolyline.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLPolyline (Private)
+
+- (NSArray<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..d439fe1645
--- /dev/null
+++ b/platform/darwin/src/MGLRasterDEMSource.h
@@ -0,0 +1,50 @@
+#import "MGLFoundation.h"
+
+#import "MGLRasterTileSource.h"
+
+/**
+ An `NSNumber` object containing an unsigned integer that specifies the encoding
+ formula for raster-dem tilesets. The integer corresponds to one of
+ the constants described in `MGLDEMEncoding`.
+
+ The default value for this option is `MGLDEMEncodingMapbox`.
+
+ This option cannot be represented in a TileJSON or style JSON file. It is used
+ with the `MGLRasterDEMSource` class and is ignored when creating an
+ `MGLRasterTileSource` or `MGLVectorTileSource` object.
+ */
+extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding;
+
+/**
+ `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 : MGLRasterTileSource
+
+@end
diff --git a/platform/darwin/src/MGLRasterDEMSource.mm b/platform/darwin/src/MGLRasterDEMSource.mm
new file mode 100644
index 0000000000..27614b9ef4
--- /dev/null
+++ b/platform/darwin/src/MGLRasterDEMSource.mm
@@ -0,0 +1,17 @@
+#import "MGLRasterDEMSource.h"
+
+#import "MGLRasterTileSource_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_Private.h b/platform/darwin/src/MGLRasterSource_Private.h
deleted file mode 100644
index 76790bd053..0000000000
--- a/platform/darwin/src/MGLRasterSource_Private.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#import "MGLRasterSource.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLRasterSource (Private)
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLRasterStyleLayer.h b/platform/darwin/src/MGLRasterStyleLayer.h
index 53a6a98b8a..bca9649e5d 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 `MGLRasterTileSource` 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 between 0 and 1 inclusive
+ * 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 between 0 and 1 inclusive
+ * 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 between −1 and 1 inclusive
+ * 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 no less than 0
+ * 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 between 0 and 1 inclusive
+ * 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 between −1 and 1 inclusive
+ * 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/MGLRasterSource.h b/platform/darwin/src/MGLRasterTileSource.h
index 4f4b7c96c3..f27cbc285f 100644
--- a/platform/darwin/src/MGLRasterSource.h
+++ b/platform/darwin/src/MGLRasterTileSource.h
@@ -8,56 +8,59 @@ NS_ASSUME_NONNULL_BEGIN
/**
An `NSNumber` object containing a floating-point number that specifies the
width and height (measured in points) at which the map displays each raster
- image tile when the map’s zoom level is an integer. The raster source scales
- its images up or down when the map’s zoom level falls between two integers.
+ image tile when the map’s zoom level is an integer. The raster tile source
+ scales its images up or down when the map’s zoom level falls between two
+ integers.
The default value for this option is 512. Version 4 of the
<a href="https://www.mapbox.com/api-documentation/#maps">Mapbox Maps API</a>
requires a value of 256, as do many third-party tile servers, so consult your
provider’s documentation for the correct value.
- This option is only applicable to `MGLRasterSource` objects; it is ignored when
- initializing `MGLVectorSource` objects.
+ This option is only applicable to `MGLRasterTileSource` objects; it is ignored
+ when initializing `MGLVectorTileSource` objects.
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileSize;
/**
- `MGLRasterSource` is a map content source that supplies raster image 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
+ `MGLRasterTileSource` is a map content source that supplies raster image 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 source is added to an `MGLStyle` object along with one or more
+ A raster tile source is added to an `MGLStyle` object along with one or more
`MGLRasterStyleLayer` objects. Use a raster style layer to control the
- appearance of content supplied by the raster source.
+ appearance of content supplied by the raster tile source.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-raster"><code>raster</code></a>
source defined by the style JSON file is represented at runtime by an
- `MGLRasterSource` object that you can use to initialize new style layers. You
+ `MGLRasterTileSource` 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:]`.
### Example
```swift
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
```
*/
MGL_EXPORT
-@interface MGLRasterSource : MGLTileSource
+@interface MGLRasterTileSource : MGLTileSource
#pragma mark Initializing a Source
/**
- Returns a raster source initialized with an identifier and configuration URL.
+ Returns a raster tile source initialized with an identifier and configuration
+ URL.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -77,13 +80,13 @@ MGL_EXPORT
which it is added.
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
- @return An initialized raster source.
+ @return An initialized raster tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL;
/**
- Returns a raster source initialized with an identifier, configuration URL, and
- tile size.
+ Returns a raster tile source initialized with an identifier, configuration URL,
+ and tile size.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -98,14 +101,14 @@ MGL_EXPORT
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
@param tileSize The width and height (measured in points) of each tiled image
- in the raster source. See the `MGLTileSourceOptionTileSize` documentation
- for details.
- @return An initialized raster source.
+ in the raster tile source. See the `MGLTileSourceOptionTileSize`
+ documentation for details.
+ @return An initialized raster tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize NS_DESIGNATED_INITIALIZER;
/**
- Returns a raster source initialized an identifier, tile URL templates, and
+ Returns a raster tile source initialized an identifier, tile URL templates, and
options.
Tile URL templates are strings that specify the URLs of the raster tile images
@@ -124,7 +127,7 @@ MGL_EXPORT
the default values.
@return An initialized tile source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options NS_DESIGNATED_INITIALIZER;
@end
diff --git a/platform/darwin/src/MGLRasterSource.mm b/platform/darwin/src/MGLRasterTileSource.mm
index 8fc18ba69d..61e9ef97fd 100644
--- a/platform/darwin/src/MGLRasterSource.mm
+++ b/platform/darwin/src/MGLRasterTileSource.mm
@@ -1,4 +1,4 @@
-#import "MGLRasterSource_Private.h"
+#import "MGLRasterTileSource_Private.h"
#import "MGLMapView_Private.h"
#import "MGLSource_Private.h"
@@ -10,16 +10,16 @@
const MGLTileSourceOption MGLTileSourceOptionTileSize = @"MGLTileSourceOptionTileSize";
-static const CGFloat MGLRasterSourceClassicTileSize = 256;
-static const CGFloat MGLRasterSourceRetinaTileSize = 512;
+static const CGFloat MGLRasterTileSourceClassicTileSize = 256;
+static const CGFloat MGLRasterTileSourceRetinaTileSize = 512;
-@interface MGLRasterSource ()
+@interface MGLRasterTileSource ()
@property (nonatomic, readonly) mbgl::style::RasterSource *rawSource;
@end
-@implementation MGLRasterSource
+@implementation MGLRasterTileSource
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
// The style specification default is 512, but 256 is the expected value for
@@ -28,21 +28,26 @@ static const CGFloat MGLRasterSourceRetinaTileSize = 512;
BOOL isMapboxURL = ([configurationURL.scheme isEqualToString:@"mapbox"]
&& [configurationURL.host containsString:@"."]
&& (!configurationURL.path.length || [configurationURL.path isEqualToString:@"/"]));
- CGFloat tileSize = isMapboxURL ? MGLRasterSourceClassicTileSize : MGLRasterSourceRetinaTileSize;
+ CGFloat tileSize = isMapboxURL ? MGLRasterTileSourceClassicTileSize : MGLRasterTileSourceRetinaTileSize;
return [self initWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize];
}
- (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)];
}
-- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options {
+- (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:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options {
mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
- uint16_t tileSize = MGLRasterSourceRetinaTileSize;
+ uint16_t tileSize = MGLRasterTileSourceRetinaTileSize;
if (NSNumber *tileSizeNumber = options[MGLTileSourceOptionTileSize]) {
if (![tileSizeNumber isKindOfClass:[NSNumber class]]) {
[NSException raise:NSInvalidArgumentException
diff --git a/platform/darwin/src/MGLRasterTileSource_Private.h b/platform/darwin/src/MGLRasterTileSource_Private.h
new file mode 100644
index 0000000000..128dcb447d
--- /dev/null
+++ b/platform/darwin/src/MGLRasterTileSource_Private.h
@@ -0,0 +1,21 @@
+#import "MGLRasterTileSource.h"
+
+#include <memory>
+
+namespace mbgl {
+ namespace style {
+ class RasterSource;
+ }
+}
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLRasterTileSource (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/MGLRendererConfiguration.h b/platform/darwin/src/MGLRendererConfiguration.h
index fbc6fc564d..34c8e9628b 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
@@ -11,7 +11,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLRendererConfiguration : NSObject
/** Returns an instance of the current renderer configuration. */
-+ (instancetype)currentConfiguration;
+@property (class, nonatomic, readonly) MGLRendererConfiguration *currentConfiguration;
/** The file source to use. Defaults to `mbgl::DefaultFileSource` */
@property (nonatomic, readonly) mbgl::DefaultFileSource *fileSource;
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/MGLShape.h b/platform/darwin/src/MGLShape.h
index e965710552..e0ef0999db 100644
--- a/platform/darwin/src/MGLShape.h
+++ b/platform/darwin/src/MGLShape.h
@@ -12,21 +12,23 @@ NS_ASSUME_NONNULL_BEGIN
Create instances of `MGLPointAnnotation`, `MGLPointCollection`, `MGLPolyline`,
`MGLMultiPolyline`, `MGLPolygon`, `MGLMultiPolygon`, or `MGLShapeCollection` in
- order to use `MGLShape`'s methods. Do not create instances of `MGLShape` directly,
- and do not create your own subclasses of this class. The shape classes correspond
- to the <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
+ order to use `MGLShape`'s methods. Do not create instances of `MGLShape`
+ directly, and do not create your own subclasses of this class. The shape
+ classes correspond to the
+ <a href="https://tools.ietf.org/html/rfc7946#section-3.1">Geometry</a> object
types in the GeoJSON standard, but some have nonstandard names for backwards
compatibility.
Although you do not create instances of this class directly, you can use its
`+[MGLShape shapeWithData:encoding:error:]` factory method to create one of the
- concrete subclasses of `MGLShape` noted above from GeoJSON data.
+ concrete subclasses of `MGLShape` noted above from GeoJSON data. To access a
+ shape’s attributes, use the corresponding `MGLFeature` class instead.
You can add shapes to the map by adding them to an `MGLShapeSource` object.
- Configure the appearance of an `MGLShapeSource`’s or `MGLVectorSource`’s shapes
- collectively using a concrete instance of `MGLVectorStyleLayer`. Alternatively,
- you can add some kinds of shapes directly to a map view as annotations or
- overlays.
+ Configure the appearance of an `MGLShapeSource`’s or `MGLVectorTileSource`’s
+ shapes collectively using a concrete instance of `MGLVectorStyleLayer`.
+ Alternatively, you can add some kinds of shapes directly to a map view as
+ annotations or overlays.
*/
MGL_EXPORT
@interface MGLShape : NSObject <MGLAnnotation, NSSecureCoding>
diff --git a/platform/darwin/src/MGLShapeCollection.h b/platform/darwin/src/MGLShapeCollection.h
index bb107ee7f0..08f3276496 100644
--- a/platform/darwin/src/MGLShapeCollection.h
+++ b/platform/darwin/src/MGLShapeCollection.h
@@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN
`MGLShapeCollection` is most commonly used to add multiple shapes to a single
`MGLShapeSource`. Configure the appearance of an `MGLShapeSource`’s or
- `MGLVectorSource`’s shape collection collectively using an
+ `MGLVectorTileSource`’s shape collection collectively using an
`MGLSymbolStyleLayer` object, or use multiple instances of
`MGLCircleStyleLayer`, `MGLFillStyleLayer`, and `MGLLineStyleLayer` to
configure the appearance of each kind of shape inside the collection.
@@ -27,7 +27,8 @@ NS_ASSUME_NONNULL_BEGIN
To represent a collection of point, polyline, or polygon shapes, it may be more
convenient to use an `MGLPointCollection`, `MGLMultiPolyline`, or
- `MGLMultiPolygon` object, respectively.
+ `MGLMultiPolygon` object, respectively. To access a shape collection’s
+ attributes, use the corresponding `MGLFeature` object.
A shape collection is known as a
<a href="https://tools.ietf.org/html/rfc7946#section-3.1.8">GeometryCollection</a>
@@ -39,7 +40,7 @@ MGL_EXPORT
/**
An array of shapes forming the shape collection.
*/
-@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLShape *) *shapes;
+@property (nonatomic, copy, readonly) NSArray<MGLShape *> *shapes;
/**
Creates and returns a shape collection consisting of the given shapes.
@@ -48,7 +49,7 @@ MGL_EXPORT
this array is copied to the new object.
@return A new shape collection object.
*/
-+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes;
++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape *> *)shapes;
@end
diff --git a/platform/darwin/src/MGLShapeCollection.mm b/platform/darwin/src/MGLShapeCollection.mm
index 03cab0043f..74e78a764a 100644
--- a/platform/darwin/src/MGLShapeCollection.mm
+++ b/platform/darwin/src/MGLShapeCollection.mm
@@ -6,11 +6,11 @@
@implementation MGLShapeCollection
-+ (instancetype)shapeCollectionWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes {
++ (instancetype)shapeCollectionWithShapes:(NSArray<MGLShape *> *)shapes {
return [[self alloc] initWithShapes:shapes];
}
-- (instancetype)initWithShapes:(NS_ARRAY_OF(MGLShape *) *)shapes {
+- (instancetype)initWithShapes:(NSArray<MGLShape *> *)shapes {
if (self = [super init]) {
_shapes = shapes.copy;
}
diff --git a/platform/darwin/src/MGLShapeSource.h b/platform/darwin/src/MGLShapeSource.h
index 7460c83f50..1fc00d4de0 100644
--- a/platform/darwin/src/MGLShapeSource.h
+++ b/platform/darwin/src/MGLShapeSource.h
@@ -1,12 +1,11 @@
-#import "MGLSource.h"
-
#import "MGLFoundation.h"
#import "MGLTypes.h"
-#import "MGLShape.h"
+#import "MGLSource.h"
NS_ASSUME_NONNULL_BEGIN
@protocol MGLFeature;
+@class MGLShape;
/**
Options for `MGLShapeSource` objects.
@@ -17,12 +16,13 @@ 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
+
+ This option 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.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClustered;
@@ -30,6 +30,9 @@ 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.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
*/
extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
@@ -38,19 +41,32 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius;
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
+
+ This option 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.
+
+ This option only affects point features within an `MGLShapeSource` object; it
+ is ignored when creating an `MGLComputedShapeSource` object.
*/
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 option 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
+
+ This option 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.
*/
@@ -61,8 +77,8 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLeve
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
+
+ This option 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.
*/
@@ -72,8 +88,8 @@ 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
+
+ This option 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.
*/
@@ -87,6 +103,10 @@ extern MGL_EXPORT const MGLShapeSourceOption MGLShapeSourceOptionSimplificationT
`MGLStyle` object along with an `MGLVectorStyleLayer` object. The vector style
layer defines the appearance of any content supplied by the shape source. You
can update a shape source by setting its `shape` or `URL` property.
+
+ `MGLShapeSource` is optimized for data sets that change dynamically and fit
+ completely in memory. For large data sets that do not fit completely in memory,
+ use the `MGLComputedShapeSource` or `MGLVectorTileSource` class.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-geojson"><code>geojson</code></a>
@@ -119,6 +139,14 @@ MGL_EXPORT
/**
Returns a shape source with an identifier, URL, and dictionary of options for
the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
@param identifier A string that uniquely identifies the source.
@param url An HTTP(S) URL, absolute file URL, or local file URL relative to the
@@ -126,11 +154,19 @@ MGL_EXPORT
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options NS_DESIGNATED_INITIALIZER;
/**
Returns a shape source with an identifier, a shape, and dictionary of options
for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
To specify attributes about the shape, use an instance of an `MGLShape`
subclass that conforms to the `MGLFeature` protocol, such as `MGLPointFeature`.
@@ -147,11 +183,19 @@ MGL_EXPORT
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options NS_DESIGNATED_INITIALIZER;
/**
Returns a shape source with an identifier, an array of features, and a dictionary
of options for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
Unlike `-initWithIdentifier:shapes:options:`, this method accepts `MGLFeature`
instances, such as `MGLPointFeature` objects, whose attributes you can use when
@@ -166,11 +210,19 @@ MGL_EXPORT
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<MGLShape<MGLFeature> *> *)features options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options;
/**
Returns a shape source with an identifier, an array of shapes, and a dictionary of
options for the source.
+
+ This class supports the following options: `MGLShapeSourceOptionClustered`,
+ `MGLShapeSourceOptionClusterRadius`,
+ `MGLShapeSourceOptionMaximumZoomLevelForClustering`,
+ `MGLShapeSourceOptionMinimumZoomLevel`, `MGLShapeSourceOptionMaximumZoomLevel`,
+ `MGLShapeSourceOptionBuffer`, and
+ `MGLShapeSourceOptionSimplificationTolerance`. Shapes provided by a shape
+ source are not clipped or wrapped automatically.
Any `MGLFeature` instance passed into this initializer is treated as an ordinary
shape, causing any attributes to be inaccessible to an `MGLVectorStyleLayer` when
@@ -186,7 +238,7 @@ MGL_EXPORT
@param options An `NSDictionary` of options for this source.
@return An initialized shape source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options;
+- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray<MGLShape *> *)shapes options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options;
#pragma mark Accessing a Source’s Content
@@ -239,7 +291,7 @@ MGL_EXPORT
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the source that match the predicate.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate;
+- (NSArray<id <MGLFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate;
@end
diff --git a/platform/darwin/src/MGLShapeSource.mm b/platform/darwin/src/MGLShapeSource.mm
index 571cbdcc62..31e5867703 100644
--- a/platform/darwin/src/MGLShapeSource.mm
+++ b/platform/darwin/src/MGLShapeSource.mm
@@ -13,13 +13,76 @@
#include <mbgl/style/sources/geojson_source.hpp>
#include <mbgl/renderer/renderer.hpp>
-const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
+const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
const MGLShapeSourceOption MGLShapeSourceOptionClusterRadius = @"MGLShapeSourceOptionClusterRadius";
-const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
+const MGLShapeSourceOption MGLShapeSourceOptionClustered = @"MGLShapeSourceOptionClustered";
const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevel = @"MGLShapeSourceOptionMaximumZoomLevel";
-const MGLShapeSourceOption MGLShapeSourceOptionBuffer = @"MGLShapeSourceOptionBuffer";
+const MGLShapeSourceOption MGLShapeSourceOptionMaximumZoomLevelForClustering = @"MGLShapeSourceOptionMaximumZoomLevelForClustering";
+const MGLShapeSourceOption MGLShapeSourceOptionMinimumZoomLevel = @"MGLShapeSourceOptionMinimumZoomLevel";
const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLShapeSourceOptionSimplificationTolerance";
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<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;
+}
+
@interface MGLShapeSource ()
@property (nonatomic, readwrite) NSDictionary *options;
@@ -29,7 +92,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
@implementation MGLShapeSource
-- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NS_DICTIONARY_OF(NSString *, id) *)options {
+- (instancetype)initWithIdentifier:(NSString *)identifier URL:(NSURL *)url options:(NSDictionary<NSString *, id> *)options {
auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options);
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, geoJSONOptions);
if (self = [super initWithPendingSource:std::move(source)]) {
@@ -38,7 +101,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
return self;
}
-- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+- (instancetype)initWithIdentifier:(NSString *)identifier shape:(nullable MGLShape *)shape options:(NSDictionary<MGLShapeSourceOption, id> *)options {
auto geoJSONOptions = MGLGeoJSONOptionsFromDictionary(options);
auto source = std::make_unique<mbgl::style::GeoJSONSource>(identifier.UTF8String, geoJSONOptions);
if (self = [super initWithPendingSource:std::move(source)]) {
@@ -47,7 +110,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
return self;
}
-- (instancetype)initWithIdentifier:(NSString *)identifier features:(NS_ARRAY_OF(MGLShape<MGLFeature> *) *)features options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+- (instancetype)initWithIdentifier:(NSString *)identifier features:(NSArray<MGLShape<MGLFeature> *> *)features options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options {
for (id <MGLFeature> feature in features) {
if (![feature conformsToProtocol:@protocol(MGLFeature)]) {
[NSException raise:NSInvalidArgumentException format:@"The object %@ included in the features argument does not conform to the MGLFeature protocol.", feature];
@@ -57,7 +120,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
return [self initWithIdentifier:identifier shape:shapeCollectionFeature options:options];
}
-- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NS_ARRAY_OF(MGLShape *) *)shapes options:(nullable NS_DICTIONARY_OF(MGLShapeSourceOption, id) *)options {
+- (instancetype)initWithIdentifier:(NSString *)identifier shapes:(NSArray<MGLShape *> *)shapes options:(nullable NSDictionary<MGLShapeSourceOption, id> *)options {
MGLShapeCollection *shapeCollection = [MGLShapeCollection shapeCollectionWithShapes:shapes];
return [self initWithIdentifier:identifier shape:shapeCollection options:options];
}
@@ -90,7 +153,7 @@ const MGLShapeSourceOption MGLShapeSourceOptionSimplificationTolerance = @"MGLSh
NSStringFromClass([self class]), (void *)self, self.identifier, self.URL, self.shape];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
+- (NSArray<id <MGLFeature>> *)featuresMatchingPredicate:(nullable NSPredicate *)predicate {
mbgl::optional<mbgl::style::Filter> optionalFilter;
if (predicate) {
@@ -105,57 +168,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..83872afcbc 100644
--- a/platform/darwin/src/MGLShapeSource_Private.h
+++ b/platform/darwin/src/MGLShapeSource_Private.h
@@ -9,10 +9,7 @@ namespace mbgl {
}
}
-@interface MGLShapeSource (Private)
-@end
-
MGL_EXPORT
-mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NS_DICTIONARY_OF(MGLShapeSourceOption, id) *options);
+mbgl::style::GeoJSONOptions MGLGeoJSONOptionsFromDictionary(NSDictionary<MGLShapeSourceOption, id> *options);
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLSource.h b/platform/darwin/src/MGLSource.h
index 8d8c936833..7bbf02fa7c 100644
--- a/platform/darwin/src/MGLSource.h
+++ b/platform/darwin/src/MGLSource.h
@@ -16,10 +16,11 @@ NS_ASSUME_NONNULL_BEGIN
add and remove sources dynamically using methods such as
`-[MGLStyle addSource:]` and `-[MGLStyle sourceWithIdentifier:]`.
- Create instances of `MGLShapeSource`, `MGLImageSource` and the concrete subclasses of
- `MGLTileSource` (`MGLVectorSource` and `MGLRasterSource`) in order to use
- `MGLSource`'s properties and methods. Do not create instances of `MGLSource`
- directly, and do not create your own subclasses of this class.
+ Create instances of `MGLShapeSource`, `MGLComputedShapeSource`,
+ `MGLImageSource`, and the concrete subclasses of `MGLTileSource`
+ (`MGLVectorTileSource` and `MGLRasterTileSource`) in order to use `MGLSource`’s
+ properties and methods. Do not create instances of `MGLSource` directly, and do
+ not create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLSource : NSObject
diff --git a/platform/darwin/src/MGLStyle.h b/platform/darwin/src/MGLStyle.h
index 0b360de8fc..814a09ed21 100644
--- a/platform/darwin/src/MGLStyle.h
+++ b/platform/darwin/src/MGLStyle.h
@@ -68,7 +68,7 @@ MGL_EXPORT
`-streetsStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)streetsStyleURL;
+@property (class, nonatomic, readonly) NSURL *streetsStyleURL;
/**
Returns the URL to the given version of the
@@ -83,13 +83,7 @@ MGL_EXPORT
*/
+ (NSURL *)streetsStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 8 of the
- <a href="https://www.mapbox.com/blog/emerald-gl/">Mapbox Emerald</a> style.
-
- Emerald is a tactile style with subtle textures and dramatic hillshading.
- */
-+ (NSURL *)emeraldStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
++ (NSURL *)emeraldStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/emerald-v8”.")));
/**
Returns the URL to the current version of the
@@ -104,7 +98,7 @@ MGL_EXPORT
`-outdoorsStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)outdoorsStyleURL;
+@property (class, nonatomic, readonly) NSURL *outdoorsStyleURL;
/**
Returns the URL to the given version of the
@@ -128,7 +122,7 @@ MGL_EXPORT
`-lightStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)lightStyleURL;
+@property (class, nonatomic, readonly) NSURL *lightStyleURL;
/**
Returns the URL to the given version of the
@@ -153,7 +147,7 @@ MGL_EXPORT
`-darkStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)darkStyleURL;
+@property (class, nonatomic, readonly) NSURL *darkStyleURL;
/**
Returns the URL to the given version of the
@@ -178,7 +172,7 @@ MGL_EXPORT
`-satelliteStyleURLWithVersion:` method instead. Such details may change
significantly from version to version.
*/
-+ (NSURL *)satelliteStyleURL;
+@property (class, nonatomic, readonly) NSURL *satelliteStyleURL;
/**
Returns the URL to the given version of the
@@ -191,16 +185,8 @@ MGL_EXPORT
*/
+ (NSURL *)satelliteStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 8 of the
- <a href="https://www.mapbox.com/maps/satellite/">Mapbox Satellite Streets</a>
- style.
- Satellite Streets combines the high-resolution satellite and aerial imagery of
- Mapbox Satellite with unobtrusive labels and translucent roads from Mapbox
- Streets.
- */
-+ (NSURL *)hybridStyleURL __attribute__((deprecated("Use -satelliteStreetsStyleURL.")));
++ (NSURL *)hybridStyleURL __attribute__((unavailable("Use -satelliteStreetsStyleURL.")));
/**
Returns the URL to the current version of the
@@ -217,7 +203,7 @@ MGL_EXPORT
`-satelliteStreetsStyleURLWithVersion:` method instead. Such details may
change significantly from version to version.
*/
-+ (NSURL *)satelliteStreetsStyleURL;
+@property (class, nonatomic, readonly) NSURL *satelliteStreetsStyleURL;
/**
Returns the URL to the given version of the
@@ -232,39 +218,14 @@ MGL_EXPORT
*/
+ (NSURL *)satelliteStreetsStyleURLWithVersion:(NSInteger)version;
-/**
- Returns the URL to version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
- style.
- */
-+ (NSURL *)trafficDayStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));
-
-/**
- Returns the URL to the given version of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Day</a>
- style as of publication.
-
- @param version A specific version of the style.
- */
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
++ (NSURL *)trafficDayStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));
-/**
- Returns the URL to the version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
- style.
++ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-day-v2”.")));;
- */
-+ (NSURL *)trafficNightStyleURL __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
++ (NSURL *)trafficNightStyleURL __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
-/**
- Returns the URL to to the version 2 of the
- <a href="https://www.mapbox.com/blog/live-traffic-maps/">Mapbox Traffic Night</a>
- style as of publication.
-
- @param version A specific version of the style.
- */
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((deprecated("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
++ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version __attribute__((unavailable("Create an NSURL object with the string “mapbox://styles/mapbox/traffic-night-v2”.")));
#pragma mark Accessing Metadata About the Style
@@ -280,7 +241,7 @@ MGL_EXPORT
/**
A set containing the style’s sources.
*/
-@property (nonatomic, strong) NS_SET_OF(__kindof MGLSource *) *sources;
+@property (nonatomic, strong) NSSet<__kindof MGLSource *> *sources;
/**
Values describing animated transitions to changes on a style's individual
@@ -342,7 +303,7 @@ MGL_EXPORT
The layers included in the style, arranged according to their back-to-front
ordering on the screen.
*/
-@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *layers;
+@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *layers;
/**
Returns a style layer with the given identifier in the current style.
@@ -455,25 +416,14 @@ MGL_EXPORT
#pragma mark Managing Style Classes
-/**
- Support for style classes has been removed. This property always returns an empty array.
- */
-@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional.")));
-/**
- Support for style classes has been removed. This method always returns NO.
- */
-- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+@property (nonatomic) NSArray<NSString *> *styleClasses __attribute__((unavailable("Support for style classes has been removed.")));
-/**
- Support for style classes has been removed. This method is a no-op.
- */
-- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
-/**
- Support for style classes has been removed. This method is a no-op.
- */
-- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (void)addStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
+
+- (void)removeStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
#pragma mark Managing a Style’s Images
@@ -532,17 +482,22 @@ MGL_EXPORT
#pragma mark Localizing Map Content
/**
- A Boolean value that determines whether the style attempts to localize labels in
- the style into the system’s preferred language.
+ Attempts to localize labels in the style into the given locale.
+
+ This method automatically modifies the text property of any symbol style layer
+ in the style whose source is the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ On iOS, the user can set the system’s preferred language in Settings, General
+ Settings, Language & Region. On macOS, the user can set the system’s preferred
+ language in the Language & Region pane of System Preferences.
- When this property is enabled, the style automatically modifies the text property
- of any symbol style layer whose source is the
- <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox
- Streets source</a>. On iOS, the user can set the system’s preferred language in
- Settings, General Settings, Language & Region. On macOS, the user can set the
- system’s preferred language in the Language & Region pane of System Preferences.
- */
-@property (nonatomic) BOOL localizesLabels;
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale;
+
+@property (nonatomic) BOOL localizesLabels __attribute__((unavailable("Use -localizeLabelsIntoLocale: instead.")));
@end
diff --git a/platform/darwin/src/MGLStyle.mm b/platform/darwin/src/MGLStyle.mm
index 987ae5f063..3f9bfbf8ca 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"
@@ -16,9 +18,9 @@
#import "MGLSource_Private.h"
#import "MGLLight_Private.h"
#import "MGLTileSource_Private.h"
-#import "MGLVectorSource.h"
-#import "MGLVectorSource+MGLAdditions.h"
-#import "MGLRasterSource.h"
+#import "MGLVectorTileSource_Private.h"
+#import "MGLRasterTileSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLShapeSource.h"
#import "MGLImageSource.h"
@@ -34,12 +36,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"
@@ -77,7 +82,8 @@
@property (nonatomic, readonly, weak) MGLMapView *mapView;
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
@property (readonly, copy, nullable) NSURL *URL;
-@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_DICTIONARY_OF(NSObject *, MGLTextLanguage *) *) *localizedLayersByIdentifier;
+@property (nonatomic, readwrite, strong) NSMutableDictionary<NSString *, MGLOpenGLStyleLayer *> *openGLLayers;
+@property (nonatomic) NSMutableDictionary<NSString *, NSDictionary<NSObject *, MGLTextLanguage *> *> *localizedLayersByIdentifier;
@end
@@ -110,64 +116,16 @@ MGL_DEFINE_STYLE(satelliteStreets, satellite-streets)
// Make sure all the styles listed in mbgl::util::default_styles::orderedStyles
// are defined above and also declared in MGLStyle.h.
-static_assert(8 == mbgl::util::default_styles::numOrderedStyles,
+static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
"mbgl::util::default_styles::orderedStyles and MGLStyle have different numbers of styles.");
-// Hybrid has been renamed Satellite Streets, so the last Hybrid version is hard-coded here.
-static NSURL *MGLStyleURL_hybrid;
-+ (NSURL *)hybridStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_hybrid = [NSURL URLWithString:@"mapbox://styles/mapbox/satellite-hybrid-v8"];
- });
- return MGLStyleURL_hybrid;
-}
-
-// Emerald is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_emerald;
-+ (NSURL *)emeraldStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_emerald = [NSURL URLWithString:@"mapbox://styles/mapbox/emerald-v8"];
- });
- return MGLStyleURL_emerald;
-}
-
-// Traffic Day is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_trafficDay;
-+ (NSURL *)trafficDayStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_trafficDay = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
- });
- return MGLStyleURL_trafficDay;
-}
-
-+ (NSURL *)trafficDayStyleURLWithVersion:(NSInteger)version {
- return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-day-v" stringByAppendingFormat:@"%li", (long)version]];
-}
-
-// Traffic Night is no longer getting new versions as a default style, so the current version is hard-coded here.
-static NSURL *MGLStyleURL_trafficNight;
-+ (NSURL *)trafficNightStyleURL {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- MGLStyleURL_trafficNight = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
- });
- return MGLStyleURL_trafficNight;
-}
-
-+ (NSURL *)trafficNightStyleURLWithVersion:(NSInteger)version {
- return [NSURL URLWithString:[@"mapbox://styles/mapbox/traffic-night-v" stringByAppendingFormat:@"%li", (long)version]];
-}
-
-
#pragma mark -
- (instancetype)initWithRawStyle:(mbgl::style::Style *)rawStyle mapView:(MGLMapView *)mapView {
if (self = [super init]) {
_mapView = mapView;
_rawStyle = rawStyle;
+ _openGLLayers = [NSMutableDictionary dictionary];
_localizedLayersByIdentifier = [NSMutableDictionary dictionary];
}
return self;
@@ -184,9 +142,9 @@ static NSURL *MGLStyleURL_trafficNight;
#pragma mark Sources
-- (NS_SET_OF(__kindof MGLSource *) *)sources {
+- (NSSet<__kindof MGLSource *> *)sources {
auto rawSources = self.rawStyle->getSources();
- NS_MUTABLE_SET_OF(__kindof MGLSource *) *sources = [NSMutableSet setWithCapacity:rawSources.size()];
+ NSMutableSet<__kindof MGLSource *> *sources = [NSMutableSet setWithCapacity:rawSources.size()];
for (auto rawSource = rawSources.begin(); rawSource != rawSources.end(); ++rawSource) {
MGLSource *source = [self sourceFromMBGLSource:*rawSource];
[sources addObject:source];
@@ -194,7 +152,7 @@ static NSURL *MGLStyleURL_trafficNight;
return sources;
}
-- (void)setSources:(NS_SET_OF(__kindof MGLSource *) *)sources {
+- (void)setSources:(NSSet<__kindof MGLSource *> *)sources {
for (MGLSource *source in self.sources) {
[self removeSource:source];
}
@@ -219,18 +177,20 @@ 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;
}
// TODO: Fill in options specific to the respective source classes
// https://github.com/mapbox/mapbox-gl-native/issues/6584
if (auto vectorSource = rawSource->as<mbgl::style::VectorSource>()) {
- return [[MGLVectorSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
+ return [[MGLVectorTileSource alloc] initWithRawSource:vectorSource mapView:self.mapView];
} else if (auto geoJSONSource = rawSource->as<mbgl::style::GeoJSONSource>()) {
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];
+ return [[MGLRasterTileSource 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 {
@@ -265,7 +225,7 @@ static NSURL *MGLStyleURL_trafficNight;
[source removeFromMapView:self.mapView];
}
-- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
+- (nullable NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
// It’d be incredibly convenient to use -sources here, but this operation
// depends on the sources being sorted in ascending order by creation, as
// with the std::vector used in mbgl.
@@ -285,10 +245,10 @@ static NSURL *MGLStyleURL_trafficNight;
#pragma mark Style layers
-- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers
+- (NSArray<__kindof MGLStyleLayer *> *)layers
{
auto layers = self.rawStyle->getLayers();
- NS_MUTABLE_ARRAY_OF(__kindof MGLStyleLayer *) *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
+ NSMutableArray<__kindof MGLStyleLayer *> *styleLayers = [NSMutableArray arrayWithCapacity:layers.size()];
for (auto layer : layers) {
MGLStyleLayer *styleLayer = [self layerFromMBGLLayer:layer];
[styleLayers addObject:styleLayer];
@@ -296,7 +256,7 @@ static NSURL *MGLStyleURL_trafficNight;
return styleLayers;
}
-- (void)setLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)layers {
+- (void)setLayers:(NSArray<__kindof MGLStyleLayer *> *)layers {
for (MGLStyleLayer *layer in self.layers) {
[self removeLayer:layer];
}
@@ -381,7 +341,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 +355,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>()) {
@@ -522,38 +486,6 @@ static NSURL *MGLStyleURL_trafficNight;
[self didChangeValueForKey:@"layers"];
}
-#pragma mark Style classes
-
-- (NS_ARRAY_OF(NSString *) *)styleClasses
-{
- return @[];
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses
-{
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
-{
-}
-
-- (NSUInteger)countOfStyleClasses {
- return 0;
-}
-
-- (BOOL)hasStyleClass:(NSString *)styleClass
-{
- return NO;
-}
-
-- (void)addStyleClass:(NSString *)styleClass
-{
-}
-
-- (void)removeStyleClass:(NSString *)styleClass
-{
-}
-
#pragma mark Style images
- (void)setImage:(MGLImage *)image forName:(NSString *)name
@@ -638,122 +570,37 @@ static NSURL *MGLStyleURL_trafficNight;
#pragma mark Mapbox Streets source introspection
-- (void)setLocalizesLabels:(BOOL)localizesLabels
-{
- if (_localizesLabels != localizesLabels) {
- _localizesLabels = localizesLabels;
- } else {
- return;
- }
+- (void)localizeLabelsIntoLocale:(nullable NSLocale *)locale {
+ NSSet<MGLVectorTileSource *> *streetsSources =
+ [self.sources filteredSetUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(MGLVectorTileSource * _Nullable source, NSDictionary<NSString *, id> * _Nullable bindings) {
+ return [source isKindOfClass:[MGLVectorTileSource class]] && [source isMapboxStreets];
+ }]];
+ NSSet<NSString *> *streetsSourceIdentifiers = [streetsSources valueForKey:@"identifier"];
- if (_localizesLabels) {
- NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage];
- NSMutableDictionary *localizedKeysByKeyBySourceIdentifier = [NSMutableDictionary dictionary];
- for (MGLSymbolStyleLayer *layer in self.layers) {
- if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
- continue;
- }
-
- MGLVectorSource *source = (MGLVectorSource *)[self sourceWithIdentifier:layer.sourceIdentifier];
- if (![source isKindOfClass:[MGLVectorSource class]] || !source.mapboxStreets) {
- continue;
- }
-
- NSDictionary *localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier];
- if (!localizedKeysByKey) {
- localizedKeysByKey = localizedKeysByKeyBySourceIdentifier[layer.sourceIdentifier] = [source localizedKeysByKeyForPreferredLanguage:preferredLanguage];
- }
-
- NSString *(^stringByLocalizingString)(NSString *) = ^ NSString * (NSString *string) {
- NSMutableString *localizedString = string.mutableCopy;
- [localizedKeysByKey enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, NSString * _Nonnull localizedKey, BOOL * _Nonnull stop) {
- NSAssert([key isKindOfClass:[NSString class]], @"key is not a string");
- NSAssert([localizedKey isKindOfClass:[NSString class]], @"localizedKey is not a string");
- [localizedString replaceOccurrencesOfString:[NSString stringWithFormat:@"{%@}", key]
- withString:[NSString stringWithFormat:@"{%@}", localizedKey]
- options:0
- range:NSMakeRange(0, localizedString.length)];
- }];
- return localizedString;
- };
-
- if ([layer.text isKindOfClass:[MGLConstantStyleValue class]]) {
- NSString *textField = [(MGLConstantStyleValue<NSString *> *)layer.text rawValue];
- 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];
- }
- }
- 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;
- }
+ for (MGLSymbolStyleLayer *layer in self.layers) {
+ if (![layer isKindOfClass:[MGLSymbolStyleLayer class]]) {
+ continue;
+ }
+ if (![streetsSourceIdentifiers containsObject:layer.sourceIdentifier]) {
+ continue;
}
- } 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];
- [textFields enumerateKeysAndObjectsUsingBlock:^(NSObject *originalLanguage, MGLTextLanguage *textLanguage, BOOL *done) {
- if ([textLanguage.updatedTextField isEqualToString:textField]) {
- layer.text = [MGLStyleValue<NSString *> valueWithRawValue: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];
- }
-}
-
-- (NS_SET_OF(MGLVectorSource *) *)mapboxStreetsSources {
- return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorSource * _Nonnull source, BOOL * _Nonnull stop) {
- return [source isKindOfClass:[MGLVectorSource class]] && source.mapboxStreets;
+ NSExpression *text = layer.text;
+ NSExpression *localizedText = [text mgl_expressionLocalizedIntoLocale:locale];
+ if (![localizedText isEqual:text]) {
+ layer.text = localizedText;
+ }
+ }
+}
+
+- (NSSet<MGLVectorTileSource *> *)mapboxStreetsSources {
+ return [self.sources objectsPassingTest:^BOOL (__kindof MGLVectorTileSource * _Nonnull source, BOOL * _Nonnull stop) {
+ return [source isKindOfClass:[MGLVectorTileSource class]] && source.mapboxStreets;
}];
}
-- (NS_ARRAY_OF(MGLStyleLayer *) *)placeStyleLayers {
+- (NSArray<MGLStyleLayer *> *)placeStyleLayers {
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
NSSet *placeSourceLayerIdentifiers = [NSSet setWithObjects:@"marine_label", @"country_label", @"state_label", @"place_label", @"water_label", @"poi_label", @"rail_station_label", @"mountain_peak_label", nil];
@@ -763,7 +610,7 @@ static NSURL *MGLStyleURL_trafficNight;
return [self.layers filteredArrayUsingPredicate:isPlacePredicate];
}
-- (NS_ARRAY_OF(MGLStyleLayer *) *)roadStyleLayers {
+- (NSArray<MGLStyleLayer *> *)roadStyleLayers {
NSSet *streetsSourceIdentifiers = [self.mapboxStreetsSources valueForKey:@"identifier"];
NSPredicate *isPlacePredicate = [NSPredicate predicateWithBlock:^BOOL (MGLVectorStyleLayer * _Nullable layer, NSDictionary<NSString *, id> * _Nullable bindings) {
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..42940083b5 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,37 @@ 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)) %>());
+ propertyValue = self.rawLayer->getDefault<%- camelize(originalPropertyName(property)) %>();
}
- return MGLStyleValueTransformer<<%- mbglType(property) %>, NSValue *, <%- mbglType(property) %>, MGL<%- camelize(property.name) %>>().toEnumStyleValue(propertyValue);
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
<% } 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.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,44 +159,31 @@ 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);
+<% if (property.type === 'string') { -%>
+ NSExpression *expression = MGLStyleValueTransformer<<%- valueTransformerArguments(property).join(', ') %>>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
<% } 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 +208,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..65f89c5909 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_interpolate:withCurveType: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_interpolate:withCurveType:parameters:stops: function with a curve type of “exponential”."))) = 0,
+ MGLInterpolationModeInterval __attribute__((unavailable("Use NSExpression instead, calling the mgl_step:from: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_step:from:stops: or mgl_interpolate:withCurveType: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_step:from:stops: or mgl_interpolate:withCurveType: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_step:from:stops: or mgl_interpolate:withCurveType: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_step:from:stops: or mgl_interpolate:withCurveType: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..5103b5f5cf 100644
--- a/platform/darwin/src/MGLStyleValue.mm
+++ b/platform/darwin/src/MGLStyleValue.mm
@@ -1,308 +1,46 @@
#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];
-}
-
-- (instancetype)initWithRawValue:(id)rawValue {
- if (self = [super init]) {
- _rawValue = rawValue;
- }
- 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 = @{};
- }
- return self;
-}
-
-- (instancetype)initWithInterpolationBase:(CGFloat)interpolationBase stops:(NSDictionary *)stops {
- if (self = [super init]) {
- self.interpolationBase = interpolationBase;
- self.stops = stops;
- }
- 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 (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."];
- }
- }
- }
- 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"];
- }
+id MGLJSONObjectFromMBGLValue(const mbgl::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::Value> &vector) {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:vector.size()];
+ for (auto value : vector) {
+ [array addObject:MGLJSONObjectFromMBGLValue(value)];
}
-
- 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."];
- }
+ return array;
+ }, [&](const std::unordered_map<std::string, mbgl::Value> &map) {
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:map.size()];
+ for (auto &item : map) {
+ dictionary[@(item.first.c_str())] = MGLJSONObjectFromMBGLValue(item.second);
}
- }
- return self;
+ return dictionary;
+ }, [](const auto &) -> id {
+ return nil;
+ });
}
-- (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];
+id MGLJSONObjectFromMBGLExpression(const mbgl::style::expression::Expression &mbglExpression) {
+ return MGLJSONObjectFromMBGLValue(mbglExpression.serialize());
}
-
-- (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."];
- }
- }
- }
- 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;
-}
-
-@end
diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h
index 2155c657bd..cc63793d0d 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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:MGLJSONObjectFromMBGLExpression(mbglValue.getExpression())];
}
};
};
diff --git a/platform/darwin/src/MGLStyle_Private.h b/platform/darwin/src/MGLStyle_Private.h
index 4cbe953a44..1294b9ad1c 100644
--- a/platform/darwin/src/MGLStyle_Private.h
+++ b/platform/darwin/src/MGLStyle_Private.h
@@ -14,7 +14,7 @@ namespace mbgl {
@class MGLAttributionInfo;
@class MGLMapView;
@class MGLOpenGLStyleLayer;
-@class MGLVectorSource;
+@class MGLVectorTileSource;
@class MGLVectorStyleLayer;
@interface MGLStyle (Private)
@@ -24,16 +24,16 @@ namespace mbgl {
@property (nonatomic, readonly, weak) MGLMapView *mapView;
@property (nonatomic, readonly) mbgl::style::Style *rawStyle;
-- (nullable NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration;
+- (nullable NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
+@property (nonatomic, readonly, strong) NSMutableDictionary<NSString *, MGLOpenGLStyleLayer *> *openGLLayers;
+- (void)setStyleClasses:(NSArray<NSString *> *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration;
@end
@interface MGLStyle (MGLStreetsAdditions)
-@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *placeStyleLayers;
-@property (nonatomic, readonly, copy) NS_ARRAY_OF(MGLVectorStyleLayer *) *roadStyleLayers;
+@property (nonatomic, readonly, copy) NSArray<MGLVectorStyleLayer *> *placeStyleLayers;
+@property (nonatomic, readonly, copy) NSArray<MGLVectorStyleLayer *> *roadStyleLayers;
@end
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index ffb95dfc73..e27f039b75 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.
@@ -318,9 +319,10 @@ typedef NS_ENUM(NSUInteger, MGLTextTranslationAnchor) {
An `MGLSymbolStyleLayer` is a style layer that renders icon and text labels at
points or along lines on the map.
- Use a symbol style layer to configure the visual appearance of labels for
- features in vector tiles loaded by an `MGLVectorSource` object or `MGLShape` or
- `MGLFeature` instances in an `MGLShapeSource` object.
+ Use a symbol style layer to configure the visual appearance of feature labels.
+ These features can come from vector tiles loaded by an `MGLVectorTileSource`
+ object, or they can be `MGLShape` or `MGLFeature` instances in an
+ `MGLShapeSource` or `MGLComputedShapeSource` object.
You can access an existing symbol style layer using the
`-[MGLStyle layerWithIdentifier:]` method if you know its identifier;
@@ -333,12 +335,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 +369,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 +379,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:
+
+ * 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 *> *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 +443,119 @@ 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:
+
+ * 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 *> *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 a style image to use for drawing an image background.
+
+ Use the `+[MGLStyle setImage:forName:]` method to associate an image with a
+ name that you can set this property to.
+
+ Within a constant string value, a feature attribute name enclosed in curly
+ braces (e.g., `{token}`) is replaced with the value of the named attribute.
+ Tokens inside non-constant expressions are ignored; instead, use `mgl_join:`
+ 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 +563,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 no less than 0
+ * 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 +624,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 +687,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 no less than 0
+ * 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 +737,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:
+
+ * 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 *> *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:
- * `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 *> *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:
+
+ * 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 *> *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 +899,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 no less than 0
+ * 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 +919,113 @@ 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 expression containing any of the following:
- You can set this property to an instance of:
+ * Constant numeric values no less than 1
+ * 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 *> *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.
- 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.
+ Within a constant string value, a feature attribute name enclosed in curly
+ braces (e.g., `{token}`) is replaced with the value of the named attribute.
+ Tokens inside non-constant expressions are ignored; instead, use `mgl_join:`
+ and key path expressions.
+
+ 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 +1034,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 +1098,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 +1109,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 +1138,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 no less than 0
+ * 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 +1165,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:
+
+ * 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 *> *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 +1195,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 +1265,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 +1337,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 no less than 0
+ * 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,73 +1398,75 @@ 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
@@ -1416,59 +1475,45 @@ MGL_EXPORT
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;
+ 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) NSExpression *iconColor;
#else
/**
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
`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;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
#endif
/**
@@ -1483,30 +1528,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 no less than 0
+ * 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.
@@ -1520,59 +1557,45 @@ MGL_EXPORT
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;
+ 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) NSExpression *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
+ The default value of this property is an expression that evaluates to
`NSColor.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<NSColor *> *iconHaloColor;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
#endif
/**
@@ -1587,30 +1610,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 no less than 0
+ * 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 +1637,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 between 0 and 1 inclusive
+ * 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 +1667,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 +1678,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:
+
+ * 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;
#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 +1707,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:
- * `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;
#endif
/**
@@ -1713,14 +1728,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,72 +1743,67 @@ 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;
+ 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) NSExpression *textColor;
#else
/**
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
`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;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
#endif
/**
@@ -1809,30 +1818,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 no less than 0
+ * 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.
@@ -1845,58 +1846,44 @@ MGL_EXPORT
/**
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;
+ 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) NSExpression *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
+ The default value of this property is an expression that evaluates to
`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 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;
+ You can set this property to an expression containing any of the following:
+
+ * Constant `NSColor` 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;
#endif
/**
@@ -1912,30 +1899,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 no less than 0
+ * 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 +1926,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 between 0 and 1 inclusive
+ * 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 +1956,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 +1967,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:
+
+ * 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;
#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 +1996,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:
- * `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;
#endif
/**
@@ -2038,14 +2017,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 +2032,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..0d9fac4808 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -154,754 +154,756 @@ 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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
-- (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);
+ NSExpression *expression = MGLStyleValueTransformer<std::string, NSString *>().toExpression(propertyValue);
+ return expression.mgl_expressionByReplacingTokensWithKeyPaths;
}
-- (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 +924,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 +959,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 +994,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 +1029,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 +1064,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 +1099,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 +1165,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 +1200,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 +1235,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 +1270,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 +1305,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 +1340,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/MGLTileSource.h b/platform/darwin/src/MGLTileSource.h
index 3dc268db60..667e1e66ca 100644
--- a/platform/darwin/src/MGLTileSource.h
+++ b/platform/darwin/src/MGLTileSource.h
@@ -117,6 +117,7 @@ extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionAttributionInfos;
*/
extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem;
+
/**
Tile coordinate systems that determine how tile coordinates in tile URLs are
interpreted.
@@ -141,6 +142,35 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) {
MGLTileCoordinateSystemTMS
};
+
+/**
+ An `NSNumber` object containing an unsigned integer that specifies the encoding
+ formula for raster-dem tilesets. The integer corresponds to one of
+ the constants described in `MGLDEMEncoding`.
+
+ The default value for this option is `MGLDEMEncodingMapbox`.
+
+ This option is not supported by the TileJSON spec.
+ */
+extern MGL_EXPORT const MGLTileSourceOption MGLTileSourceOptionDEMEncoding;
+
+/**
+ The encoding formula used to generate the raster-dem tileset
+*/
+
+typedef NS_ENUM(NSUInteger, MGLDEMEncoding) {
+
+ /**
+ Raster tiles generated with the [Mapbox encoding formula](https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb).
+ */
+ MGLDEMEncodingMapbox = 0,
+
+ /**
+ Raster tiles generated with the [Mapzen Terrarium encoding formula](https://aws.amazon.com/public-datasets/terrain/).
+ */
+ MGLDEMEncodingTerrarium
+};
+
/**
`MGLTileSource` is a map content source that supplies map tiles to be shown on
the map. The location of and metadata about the tiles are defined either by an
@@ -154,9 +184,9 @@ typedef NS_ENUM(NSUInteger, MGLTileCoordinateSystem) {
Mapbox-hosted tile set, view it in
<a href="https://www.mapbox.com/studio/tilesets/">Mapbox Studio’s Tilesets editor</a>.
- Create instances of `MGLRasterSource` and `MGLVectorSource` in order to use
- `MGLTileSource`'s properties and methods. Do not create instances of `MGLTileSource`
- directly, and do not create your own subclasses of this class.
+ Create instances of `MGLRasterTileSource` and `MGLVectorTileSource` in order
+ to use `MGLTileSource`'s properties and methods. Do not create instances of
+ `MGLTileSource` directly, and do not create your own subclasses of this class.
*/
MGL_EXPORT
@interface MGLTileSource : MGLSource
@@ -182,7 +212,7 @@ MGL_EXPORT
configuration URL, this array is also empty until the configuration JSON file
is loaded.
*/
-@property (nonatomic, copy, readonly) NS_ARRAY_OF(MGLAttributionInfo *) *attributionInfos;
+@property (nonatomic, copy, readonly) NSArray<MGLAttributionInfo *> *attributionInfos;
@end
diff --git a/platform/darwin/src/MGLTileSource.mm b/platform/darwin/src/MGLTileSource.mm
index bc985bd728..2fafc6fb51 100644
--- a/platform/darwin/src/MGLTileSource.mm
+++ b/platform/darwin/src/MGLTileSource.mm
@@ -2,6 +2,7 @@
#import "MGLAttributionInfo_Private.h"
#import "MGLGeometry_Private.h"
+#import "MGLRasterDEMSource.h"
#import "NSString+MGLAdditions.h"
#import "NSValue+MGLAdditions.h"
@@ -19,6 +20,7 @@ const MGLTileSourceOption MGLTileSourceOptionCoordinateBounds = @"MGLTileSourceO
const MGLTileSourceOption MGLTileSourceOptionAttributionHTMLString = @"MGLTileSourceOptionAttributionHTMLString";
const MGLTileSourceOption MGLTileSourceOptionAttributionInfos = @"MGLTileSourceOptionAttributionInfos";
const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSourceOptionTileCoordinateSystem";
+const MGLTileSourceOption MGLTileSourceOptionDEMEncoding = @"MGLTileSourceOptionDEMEncoding";
@implementation MGLTileSource
@@ -28,11 +30,11 @@ const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSou
return nil;
}
-- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfos {
+- (NSArray<MGLAttributionInfo *> *)attributionInfos {
return [self attributionInfosWithFontSize:0 linkColor:nil];
}
-- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
+- (NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor {
return [MGLAttributionInfo attributionInfosFromHTMLString:self.attributionHTMLString
fontSize:fontSize
linkColor:linkColor];
@@ -46,7 +48,7 @@ const MGLTileSourceOption MGLTileSourceOptionTileCoordinateSystem = @"MGLTileSou
@end
-mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options) {
+mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MGLTileSourceOption, id> * _Nullable options) {
mbgl::Tileset tileSet;
for (NSString *tileURLTemplate in tileURLTemplates) {
@@ -129,5 +131,22 @@ mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTem
}
}
+ if (NSNumber *demEncodingNumber = options[MGLTileSourceOptionDEMEncoding]) {
+ if (![demEncodingNumber isKindOfClass:[NSValue class]]) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"MGLTileSourceOptionDEMEncoding must be set to an NSValue or NSNumber."];
+ }
+ MGLDEMEncoding demEncoding;
+ [demEncodingNumber getValue:&demEncoding];
+ switch (demEncoding) {
+ case MGLDEMEncodingMapbox:
+ tileSet.encoding = mbgl::Tileset::DEMEncoding::Mapbox;
+ break;
+ case MGLDEMEncodingTerrarium:
+ tileSet.encoding = mbgl::Tileset::DEMEncoding::Terrarium;
+ break;
+ }
+ }
+
return tileSet;
}
diff --git a/platform/darwin/src/MGLTileSource_Private.h b/platform/darwin/src/MGLTileSource_Private.h
index 0d9876d412..1b260ca86a 100644
--- a/platform/darwin/src/MGLTileSource_Private.h
+++ b/platform/darwin/src/MGLTileSource_Private.h
@@ -28,11 +28,11 @@ namespace mbgl {
@param fontSize The default text size in points, or 0 to use the default.
@param linkColor The default link color, or `nil` to use the default.
*/
-- (NS_ARRAY_OF(MGLAttributionInfo *) *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
+- (NSArray<MGLAttributionInfo *> *)attributionInfosWithFontSize:(CGFloat)fontSize linkColor:(nullable MGLColor *)linkColor;
@end
MGL_EXPORT
-mbgl::Tileset MGLTileSetFromTileURLTemplates(NS_ARRAY_OF(NSString *) *tileURLTemplates, NS_DICTIONARY_OF(MGLTileSourceOption, id) * _Nullable options);
+mbgl::Tileset MGLTileSetFromTileURLTemplates(NSArray<NSString *> *tileURLTemplates, NSDictionary<MGLTileSourceOption, id> * _Nullable options);
NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLTypes.h b/platform/darwin/src/MGLTypes.h
index 5c32791c2f..fbc3a43ea2 100644
--- a/platform/darwin/src/MGLTypes.h
+++ b/platform/darwin/src/MGLTypes.h
@@ -111,23 +111,3 @@ NS_INLINE MGLTransition MGLTransitionMake(NSTimeInterval duration, NSTimeInterva
}
NS_ASSUME_NONNULL_END
-
-#ifndef NS_ARRAY_OF
- // Foundation collection classes adopted lightweight generics in iOS 9.0 and OS X 10.11 SDKs.
- #if __has_feature(objc_generics) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 || __MAC_OS_X_VERSION_MAX_ALLOWED >= 101100)
- /** Inserts a type specifier for a pointer to a lightweight generic with the given collection and object classes. Use a `*` for any non-`id` object classes but no `*` for the collection class. */
- #define NS_ARRAY_OF(ObjectClass...) NSArray <ObjectClass>
- #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray <ObjectClass>
- #define NS_SET_OF(ObjectClass...) NSSet <ObjectClass>
- #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet <ObjectClass>
- #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary <ObjectClass>
- #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary <ObjectClass>
- #else
- #define NS_ARRAY_OF(ObjectClass...) NSArray
- #define NS_MUTABLE_ARRAY_OF(ObjectClass...) NSMutableArray
- #define NS_SET_OF(ObjectClass...) NSSet
- #define NS_MUTABLE_SET_OF(ObjectClass...) NSMutableSet
- #define NS_DICTIONARY_OF(ObjectClass...) NSDictionary
- #define NS_MUTABLE_DICTIONARY_OF(ObjectClass...) NSMutableDictionary
- #endif
-#endif
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
deleted file mode 100644
index b1bda56f2d..0000000000
--- a/platform/darwin/src/MGLVectorSource.mm
+++ /dev/null
@@ -1,73 +0,0 @@
-#import "MGLVectorSource_Private.h"
-
-#import "MGLFeature_Private.h"
-#import "MGLSource_Private.h"
-#import "MGLTileSource_Private.h"
-#import "MGLStyle_Private.h"
-#import "MGLMapView_Private.h"
-#import "NSPredicate+MGLAdditions.h"
-#import "NSURL+MGLAdditions.h"
-
-#include <mbgl/map/map.hpp>
-#include <mbgl/style/sources/vector_source.hpp>
-#include <mbgl/renderer/renderer.hpp>
-
-@interface MGLVectorSource ()
-
-@property (nonatomic, readonly) mbgl::style::VectorSource *rawSource;
-
-@end
-
-@implementation MGLVectorSource
-
-- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
- auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String,
- configurationURL.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
- return self = [super initWithPendingSource:std::move(source)];
-}
-
-- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options {
- mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
- auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String, tileSet);
- return self = [super initWithPendingSource:std::move(source)];
-}
-
-- (mbgl::style::VectorSource *)rawSource {
- return (mbgl::style::VectorSource *)super.rawSource;
-}
-
-- (NSURL *)configurationURL {
- auto url = self.rawSource->getURL();
- return url ? [NSURL URLWithString:@(url->c_str())] : nil;
-}
-
-- (NSString *)attributionHTMLString {
- auto attribution = self.rawSource->getAttribution();
- return attribution ? @(attribution->c_str()) : nil;
-}
-
-- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate {
-
- mbgl::optional<std::vector<std::string>> optionalSourceLayerIDs;
- if (sourceLayerIdentifiers) {
- __block std::vector<std::string> layerIDs;
- layerIDs.reserve(sourceLayerIdentifiers.count);
- [sourceLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
- layerIDs.push_back(identifier.UTF8String);
- }];
- optionalSourceLayerIDs = layerIDs;
- }
-
- mbgl::optional<mbgl::style::Filter> optionalFilter;
- if (predicate) {
- optionalFilter = predicate.mgl_filter;
- }
-
- std::vector<mbgl::Feature> features;
- if (self.mapView) {
- features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
- }
- return MGLFeaturesFromMBGLFeatures(features);
-}
-
-@end
diff --git a/platform/darwin/src/MGLVectorSource_Private.h b/platform/darwin/src/MGLVectorSource_Private.h
deleted file mode 100644
index 335743173e..0000000000
--- a/platform/darwin/src/MGLVectorSource_Private.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#import "MGLVectorSource.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLVectorSource (Private)
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/darwin/src/MGLVectorStyleLayer.h b/platform/darwin/src/MGLVectorStyleLayer.h
index 6603570e25..d9431215a1 100644
--- a/platform/darwin/src/MGLVectorStyleLayer.h
+++ b/platform/darwin/src/MGLVectorStyleLayer.h
@@ -7,13 +7,13 @@ NS_ASSUME_NONNULL_BEGIN
/**
`MGLVectorStyleLayer` is an abstract superclass for style layers whose content
- is defined by an `MGLShapeSource` or `MGLVectorSource` object.
+ is defined by an `MGLShapeSource` or `MGLVectorTileSource` 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
@@ -146,7 +47,7 @@ MGL_EXPORT
```swift
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
layer.sourceLayerIdentifier = "contours"
- layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
mapView.style?.addLayer(layer)
```
*/
diff --git a/platform/darwin/src/MGLVectorSource.h b/platform/darwin/src/MGLVectorTileSource.h
index 968be3c0e0..70d2f6e8ec 100644
--- a/platform/darwin/src/MGLVectorSource.h
+++ b/platform/darwin/src/MGLVectorTileSource.h
@@ -5,29 +5,37 @@
NS_ASSUME_NONNULL_BEGIN
/**
- `MGLVectorSource` is a map content source that supplies tiled vector data in
- <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tile</a> format 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
+ `MGLVectorTileSource` is a map content source that supplies tiled vector data
+ in <a href="https://www.mapbox.com/vector-tiles/">Mapbox Vector Tile</a> format
+ 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 vector source is added to an `MGLStyle` object along with one or more
+ A vector tile source is added to an `MGLStyle` object along with one or more
`MGLVectorStyleLayer` objects. A vector style layer defines the appearance of
- any content supplied by the vector source.
+ any content supplied by the vector tile source.
+
+ `MGLVectorTileSource` is optimized for data sets that are too large to fit
+ completely in memory, such as vector tile sets or data sets managed in
+ <a href="https://www.mapbox.com/studio/">Mapbox Studio</a>. For
+ <a href="http://geojson.org/">GeoJSON</a> data, use the `MGLShapeSource`
+ class. For tiled data that changes dynamically, the `MGLComputedShapeSource`
+ class may be a suitable alternative.
Each
<a href="https://www.mapbox.com/mapbox-gl-style-spec/#sources-vector"><code>vector</code></a>
source defined by the style JSON file is represented at runtime by an
- `MGLVectorSource` object that you can use to initialize new style layers. You
- can also add and remove sources dynamically using methods such as
+ `MGLVectorTileSource` 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:]`.
Within each vector tile, each geometric coordinate must lie between
−1&nbsp;×&nbsp;<var>extent</var> and
(<var>extent</var>&nbsp;×&nbsp;2)&nbsp;−&nbsp;1, inclusive. Any vector style
- layer initialized with a vector source must have a non-`nil` value in its
+ layer initialized with a vector tile source must have a non-`nil` value in its
`sourceLayerIdentifier` property.
- Commonly used vector sources include
+ Commonly used vector tile sources include
<a href="https://www.mapbox.com/vector-tiles/mapbox-streets/">Mapbox Streets</a>,
<a href="https://www.mapbox.com/vector-tiles/mapbox-terrain/">Mapbox Terrain</a>,
and
@@ -36,23 +44,24 @@ NS_ASSUME_NONNULL_BEGIN
### Example
```swift
- let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ let source = MGLVectorTileSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
```
*/
MGL_EXPORT
-@interface MGLVectorSource : MGLTileSource
+@interface MGLVectorTileSource : MGLTileSource
#pragma mark Initializing a Source
/**
- Returns a vector source initialized with an identifier and configuration URL.
+ Returns a vector tile source initialized with an identifier and configuration
+ URL.
After initializing and configuring the source, add it to a map view’s style
using the `-[MGLStyle addSource:]` method.
@@ -66,12 +75,12 @@ MGL_EXPORT
which it is added.
@param configurationURL A URL to a TileJSON configuration file describing the
source’s contents and other metadata.
- @return An initialized vector source.
+ @return An initialized vector tile source.
*/
- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL NS_DESIGNATED_INITIALIZER;
/**
- Returns a vector source initialized an identifier, tile URL templates, and
+ Returns a vector tile source initialized an identifier, tile URL templates, and
options.
Tile URL templates are strings that specify the URLs of the vector tiles to
@@ -90,7 +99,7 @@ MGL_EXPORT
the default values.
@return An initialized tile source.
*/
-- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NS_ARRAY_OF(NSString *) *)tileURLTemplates options:(nullable NS_DICTIONARY_OF(MGLTileSourceOption, id) *)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options NS_DESIGNATED_INITIALIZER;
#pragma mark Accessing a Source’s Content
@@ -129,7 +138,7 @@ MGL_EXPORT
@return An array of objects conforming to the `MGLFeature` protocol that
represent features loaded by the source that match the predicate.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)featuresInSourceLayersWithIdentifiers:(NS_SET_OF(NSString *) *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:));
+- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(features(sourceLayerIdentifiers:predicate:));
@end
diff --git a/platform/darwin/src/MGLVectorTileSource.mm b/platform/darwin/src/MGLVectorTileSource.mm
new file mode 100644
index 0000000000..c6715d1759
--- /dev/null
+++ b/platform/darwin/src/MGLVectorTileSource.mm
@@ -0,0 +1,153 @@
+#import "MGLVectorTileSource_Private.h"
+
+#import "MGLFeature_Private.h"
+#import "MGLSource_Private.h"
+#import "MGLTileSource_Private.h"
+#import "MGLStyle_Private.h"
+#import "MGLMapView_Private.h"
+
+#import "NSPredicate+MGLAdditions.h"
+#import "NSURL+MGLAdditions.h"
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/sources/vector_source.hpp>
+#include <mbgl/renderer/renderer.hpp>
+
+@interface MGLVectorTileSource ()
+
+@property (nonatomic, readonly) mbgl::style::VectorSource *rawSource;
+
+@end
+
+@implementation MGLVectorTileSource
+
+- (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL {
+ auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String,
+ configurationURL.mgl_URLByStandardizingScheme.absoluteString.UTF8String);
+ return self = [super initWithPendingSource:std::move(source)];
+}
+
+- (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options {
+ mbgl::Tileset tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, options);
+ auto source = std::make_unique<mbgl::style::VectorSource>(identifier.UTF8String, tileSet);
+ return self = [super initWithPendingSource:std::move(source)];
+}
+
+- (mbgl::style::VectorSource *)rawSource {
+ return (mbgl::style::VectorSource *)super.rawSource;
+}
+
+- (NSURL *)configurationURL {
+ auto url = self.rawSource->getURL();
+ return url ? [NSURL URLWithString:@(url->c_str())] : nil;
+}
+
+- (NSString *)attributionHTMLString {
+ auto attribution = self.rawSource->getAttribution();
+ return attribution ? @(attribution->c_str()) : nil;
+}
+
+- (NSArray<id <MGLFeature>> *)featuresInSourceLayersWithIdentifiers:(NSSet<NSString *> *)sourceLayerIdentifiers predicate:(nullable NSPredicate *)predicate {
+
+ mbgl::optional<std::vector<std::string>> optionalSourceLayerIDs;
+ if (sourceLayerIdentifiers) {
+ __block std::vector<std::string> layerIDs;
+ layerIDs.reserve(sourceLayerIdentifiers.count);
+ [sourceLayerIdentifiers enumerateObjectsUsingBlock:^(NSString * _Nonnull identifier, BOOL * _Nonnull stop) {
+ layerIDs.push_back(identifier.UTF8String);
+ }];
+ optionalSourceLayerIDs = layerIDs;
+ }
+
+ mbgl::optional<mbgl::style::Filter> optionalFilter;
+ if (predicate) {
+ optionalFilter = predicate.mgl_filter;
+ }
+
+ std::vector<mbgl::Feature> features;
+ if (self.mapView) {
+ features = self.mapView.renderer->querySourceFeatures(self.rawSource->getID(), { optionalSourceLayerIDs, optionalFilter });
+ }
+ return MGLFeaturesFromMBGLFeatures(features);
+}
+
+@end
+
+@implementation MGLVectorTileSource (Private)
+
+/**
+ An array of locale codes with dedicated name fields in the Mapbox Streets
+ source.
+
+ https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview
+ */
+static NSArray * const MGLMapboxStreetsLanguages = @[
+ @"ar", @"de", @"en", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+
+/**
+ Like `MGLMapboxStreetsLanguages`, but deanglicized for use with
+ `+[NSBundle preferredLocalizationsFromArray:forPreferences:]`.
+ */
+static NSArray * const MGLMapboxStreetsAlternativeLanguages = @[
+ @"mul", @"ar", @"de", @"es", @"fr", @"pt", @"ru", @"zh", @"zh-Hans",
+];
+
++ (NSSet<NSString *> *)mapboxStreetsLanguages {
+ static dispatch_once_t onceToken;
+ static NSSet<NSString *> *mapboxStreetsLanguages;
+ dispatch_once(&onceToken, ^{
+ mapboxStreetsLanguages = [NSSet setWithArray:MGLMapboxStreetsLanguages];
+ });
+ return mapboxStreetsLanguages;
+}
+
++ (NSString *)preferredMapboxStreetsLanguage {
+ return [self preferredMapboxStreetsLanguageForPreferences:[NSLocale preferredLanguages]];
+}
+
++ (NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray {
+ BOOL acceptsEnglish = [preferencesArray filteredArrayUsingPredicate:
+ [NSPredicate predicateWithBlock:^BOOL(NSString * _Nullable language, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [[NSLocale localeWithLocaleIdentifier:language].languageCode isEqualToString:@"en"];
+ }]].count;
+
+ NSArray<NSString *> *preferredLanguages = [NSBundle preferredLocalizationsFromArray:MGLMapboxStreetsAlternativeLanguages
+ forPreferences:preferencesArray];
+ NSString *mostSpecificLanguage;
+ for (NSString *language in preferredLanguages) {
+ if (language.length > mostSpecificLanguage.length) {
+ mostSpecificLanguage = language;
+ }
+ }
+ if ([mostSpecificLanguage isEqualToString:@"mul"]) {
+ return acceptsEnglish ? @"en" : nil;
+ }
+ return mostSpecificLanguage;
+}
+
+- (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"];
+}
+
+- (NSDictionary<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 [MGLVectorTileSource mapboxStreetsLanguages]) {
+ NSString *key = [NSString stringWithFormat:@"name_%@", languageCode];
+ localizedKeysByKey[key] = localizedKey;
+ }
+ return localizedKeysByKey;
+}
+
+@end
diff --git a/platform/darwin/src/MGLVectorTileSource_Private.h b/platform/darwin/src/MGLVectorTileSource_Private.h
new file mode 100644
index 0000000000..8042e2cfee
--- /dev/null
+++ b/platform/darwin/src/MGLVectorTileSource_Private.h
@@ -0,0 +1,18 @@
+#import "MGLVectorTileSource.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface MGLVectorTileSource (Private)
+
+@property (nonatomic, readonly, getter=isMapboxStreets) BOOL mapboxStreets;
+
++ (NSSet<NSString *> *)mapboxStreetsLanguages;
+
++ (nullable NSString *)preferredMapboxStreetsLanguage;
++ (nullable NSString *)preferredMapboxStreetsLanguageForPreferences:(NSArray<NSString *> *)preferencesArray;
+
+- (NSDictionary<NSString *, NSString *> *)localizedKeysByKeyForPreferredLanguage:(nullable NSString *)preferredLanguage;
+
+@end
+
+NS_ASSUME_NONNULL_END
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/NSBundle+MGLAdditions.h b/platform/darwin/src/NSBundle+MGLAdditions.h
index ad5e9d5369..86dc27f22c 100644
--- a/platform/darwin/src/NSBundle+MGLAdditions.h
+++ b/platform/darwin/src/NSBundle+MGLAdditions.h
@@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (nullable NSString *)mgl_frameworkBundleIdentifier;
-+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary;
++ (nullable NSDictionary<NSString *, id> *)mgl_frameworkInfoDictionary;
+ (nullable NSString *)mgl_applicationBundleIdentifier;
diff --git a/platform/darwin/src/NSBundle+MGLAdditions.m b/platform/darwin/src/NSBundle+MGLAdditions.m
index f55059324e..37f78963d3 100644
--- a/platform/darwin/src/NSBundle+MGLAdditions.m
+++ b/platform/darwin/src/NSBundle+MGLAdditions.m
@@ -26,7 +26,7 @@
return self.mgl_frameworkInfoDictionary[@"CFBundleIdentifier"];
}
-+ (nullable NS_DICTIONARY_OF(NSString *, id) *)mgl_frameworkInfoDictionary {
++ (nullable NSDictionary<NSString *, id> *)mgl_frameworkInfoDictionary {
NSBundle *bundle = self.mgl_frameworkBundle;
return bundle.infoDictionary;
}
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index ac2d598d05..380215ff32 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -1,231 +1,12 @@
#import "NSComparisonPredicate+MGLAdditions.h"
+#import "MGLStyleValue_Private.h"
+
#import "NSPredicate+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
@implementation NSComparisonPredicate (MGLAdditions)
-- (mbgl::style::Filter)mgl_filter {
- NSExpression *leftExpression = self.leftExpression;
- NSExpression *rightExpression = self.rightExpression;
- NSExpressionType leftType = leftExpression.expressionType;
- NSExpressionType rightType = rightExpression.expressionType;
- BOOL isReversed = ((leftType == NSConstantValueExpressionType || leftType == NSAggregateExpressionType)
- && rightType == NSKeyPathExpressionType);
- switch (self.predicateOperatorType) {
- case NSEqualToPredicateOperatorType: {
- mbgl::style::EqualsFilter eqFilter;
- eqFilter.key = self.mgl_keyPath.UTF8String;
- eqFilter.value = self.mgl_constantValue;
-
- // Convert $type == to TypeEqualsFilter.
- if (eqFilter.key == "$type") {
- mbgl::style::TypeEqualsFilter typeEqFilter;
- typeEqFilter.value = self.mgl_featureType;
- return typeEqFilter;
- }
-
- // Convert $id == to IdentifierEqualsFilter.
- if (eqFilter.key == "$id") {
- // Convert $id == nil to NotHasIdentifierFilter.
- if (eqFilter.value.is<mbgl::NullValue>()) {
- return mbgl::style::NotHasIdentifierFilter();
- }
-
- mbgl::style::IdentifierEqualsFilter idEqFilter;
- idEqFilter.value = self.mgl_featureIdentifier;
- return idEqFilter;
- }
-
- // Convert == nil to NotHasFilter.
- if (eqFilter.value.is<mbgl::NullValue>()) {
- mbgl::style::NotHasFilter notHasFilter;
- notHasFilter.key = eqFilter.key;
- return notHasFilter;
- }
-
- return eqFilter;
- }
- case NSNotEqualToPredicateOperatorType: {
- mbgl::style::NotEqualsFilter neFilter;
- neFilter.key = self.mgl_keyPath.UTF8String;
- neFilter.value = self.mgl_constantValue;
-
- // Convert $type != to TypeNotEqualsFilter.
- if (neFilter.key == "$type") {
- mbgl::style::TypeNotEqualsFilter typeNeFilter;
- typeNeFilter.value = self.mgl_featureType;
- return typeNeFilter;
- }
-
- // Convert $id != to IdentifierNotEqualsFilter.
- if (neFilter.key == "$id") {
- // Convert $id != nil to HasIdentifierFilter.
- if (neFilter.value.is<mbgl::NullValue>()) {
- return mbgl::style::HasIdentifierFilter();
- }
-
- mbgl::style::IdentifierNotEqualsFilter idNeFilter;
- idNeFilter.value = self.mgl_featureIdentifier;
- return idNeFilter;
- }
-
- // Convert != nil to HasFilter.
- if (neFilter.value.is<mbgl::NullValue>()) {
- mbgl::style::HasFilter hasFilter;
- hasFilter.key = neFilter.key;
- return hasFilter;
- }
-
- return neFilter;
- }
- case NSGreaterThanPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::LessThanFilter ltFilter;
- ltFilter.key = self.mgl_keyPath.UTF8String;
- ltFilter.value = self.mgl_constantValue;
- return ltFilter;
- } else {
- mbgl::style::GreaterThanFilter gtFilter;
- gtFilter.key = self.mgl_keyPath.UTF8String;
- gtFilter.value = self.mgl_constantValue;
- return gtFilter;
- }
- }
- case NSGreaterThanOrEqualToPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = self.mgl_keyPath.UTF8String;
- lteFilter.value = self.mgl_constantValue;
- return lteFilter;
- } else {
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = self.mgl_keyPath.UTF8String;
- gteFilter.value = self.mgl_constantValue;
- return gteFilter;
- }
- }
- case NSLessThanPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::GreaterThanFilter gtFilter;
- gtFilter.key = self.mgl_keyPath.UTF8String;
- gtFilter.value = self.mgl_constantValue;
- return gtFilter;
- } else {
- mbgl::style::LessThanFilter ltFilter;
- ltFilter.key = self.mgl_keyPath.UTF8String;
- ltFilter.value = self.mgl_constantValue;
- return ltFilter;
- }
- }
- case NSLessThanOrEqualToPredicateOperatorType: {
- if (isReversed) {
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = self.mgl_keyPath.UTF8String;
- gteFilter.value = self.mgl_constantValue;
- return gteFilter;
- } else {
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = self.mgl_keyPath.UTF8String;
- lteFilter.value = self.mgl_constantValue;
- return lteFilter;
- }
- }
- case NSInPredicateOperatorType: {
- if (isReversed) {
- if (leftType == NSConstantValueExpressionType && [leftExpression.constantValue isKindOfClass:[NSString class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"CONTAINS not supported for string comparison."];
- }
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare values IN attribute."];
- }
-
- // Convert $type IN to TypeInFilter.
- if ([leftExpression.keyPath isEqualToString:@"$type"]) {
- mbgl::style::TypeInFilter typeInFilter;
- typeInFilter.values = rightExpression.mgl_aggregateFeatureType;
- return typeInFilter;
- }
-
- // Convert $id IN to IdentifierInFilter.
- if ([leftExpression.keyPath isEqualToString:@"$id"]) {
- mbgl::style::IdentifierInFilter idInFilter;
- idInFilter.values = rightExpression.mgl_aggregateFeatureIdentifier;
- return idInFilter;
- }
-
- mbgl::style::InFilter inFilter;
- inFilter.key = leftExpression.keyPath.UTF8String;
- inFilter.values = rightExpression.mgl_aggregateMBGLValue;
- return inFilter;
- }
- case NSContainsPredicateOperatorType: {
- if (!isReversed) {
- if (rightType == NSConstantValueExpressionType && [rightExpression.constantValue isKindOfClass:[NSString class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"IN not supported for string comparison."];
- }
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare attribute CONTAINS values."];
- }
-
- // Convert CONTAINS $type to TypeInFilter.
- if ([rightExpression.keyPath isEqualToString:@"$type"]) {
- mbgl::style::TypeInFilter typeInFilter;
- typeInFilter.values = leftExpression.mgl_aggregateFeatureType;
- return typeInFilter;
- }
-
- // Convert CONTAINS $id to IdentifierInFilter.
- if ([rightExpression.keyPath isEqualToString:@"$id"]) {
- mbgl::style::IdentifierInFilter idInFilter;
- idInFilter.values = leftExpression.mgl_aggregateFeatureIdentifier;
- return idInFilter;
- }
-
- mbgl::style::InFilter inFilter;
- inFilter.key = rightExpression.keyPath.UTF8String;
- inFilter.values = leftExpression.mgl_aggregateMBGLValue;
- return inFilter;
- }
- case NSBetweenPredicateOperatorType: {
- if (isReversed) {
- [NSException raise:NSInvalidArgumentException
- format:@"Predicate cannot compare bounds BETWEEN attribute."];
- }
- if (![rightExpression.constantValue isKindOfClass:[NSArray class]]) {
- [NSException raise:NSInvalidArgumentException
- format:@"Right side of BETWEEN predicate must be an array."]; // not NSSet
- }
- auto values = rightExpression.mgl_aggregateMBGLValue;
- if (values.size() != 2) {
- [NSException raise:NSInvalidArgumentException
- format:@"Right side of BETWEEN predicate must have two items."];
- }
- mbgl::style::AllFilter allFilter;
- mbgl::style::GreaterThanEqualsFilter gteFilter;
- gteFilter.key = leftExpression.keyPath.UTF8String;
- gteFilter.value = values[0];
- allFilter.filters.push_back(gteFilter);
- mbgl::style::LessThanEqualsFilter lteFilter;
- lteFilter.key = leftExpression.keyPath.UTF8String;
- lteFilter.value = values[1];
- allFilter.filters.push_back(lteFilter);
- return allFilter;
- }
- case NSMatchesPredicateOperatorType:
- case NSLikePredicateOperatorType:
- case NSBeginsWithPredicateOperatorType:
- case NSEndsWithPredicateOperatorType:
- case NSCustomSelectorPredicateOperatorType:
- [NSException raise:NSInvalidArgumentException
- format:@"NSPredicateOperatorType:%lu is not supported.", (unsigned long)self.predicateOperatorType];
- }
-
- return {};
-}
-
- (NSString *)mgl_keyPath {
NSExpression *leftExpression = self.leftExpression;
NSExpression *rightExpression = self.rightExpression;
@@ -294,3 +75,76 @@
}
@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 NSBetweenPredicateOperatorType: {
+ op = @"all";
+ NSArray *limits = self.rightExpression.constantValue;
+ NSPredicate *leftHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:limits[0]
+ rightExpression:self.leftExpression
+ modifier:NSDirectPredicateModifier
+ type:NSLessThanOrEqualToPredicateOperatorType
+ options:0];
+ NSPredicate *rightHandPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.leftExpression
+ rightExpression:limits[1]
+ modifier:NSDirectPredicateModifier
+ type:NSLessThanOrEqualToPredicateOperatorType
+ options:0];
+ return @[op, leftHandPredicate.mgl_jsonExpressionObject, rightHandPredicate.mgl_jsonExpressionObject];
+ }
+ case NSInPredicateOperatorType: {
+ NSMutableArray *elements = [NSMutableArray arrayWithObjects:@"match", self.leftExpression.mgl_jsonExpressionObject, nil];
+ NSArray *optionsExpressions = self.rightExpression.constantValue;
+ for (id object in optionsExpressions) {
+ id option = ((NSExpression *)object).mgl_jsonExpressionObject;
+ [elements addObject:option];
+ [elements addObject:@YES];
+ }
+ [elements addObject:@NO];
+ return elements;
+ }
+ case NSContainsPredicateOperatorType: {
+ NSPredicate *inPredicate = [NSComparisonPredicate predicateWithLeftExpression:self.rightExpression
+ rightExpression:self.leftExpression
+ modifier:self.comparisonPredicateModifier
+ type:NSInPredicateOperatorType
+ options:self.options];
+ return inPredicate.mgl_jsonExpressionObject;
+ }
+ case NSMatchesPredicateOperatorType:
+ case NSLikePredicateOperatorType:
+ case NSBeginsWithPredicateOperatorType:
+ case NSEndsWithPredicateOperatorType:
+ case NSCustomSelectorPredicateOperatorType:
+ [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..5a98b763ea 100644
--- a/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSCompoundPredicate+MGLAdditions.mm
@@ -1,7 +1,11 @@
#import "NSCompoundPredicate+MGLAdditions.h"
+#import "MGLStyleValue_Private.h"
+
#import "NSPredicate+MGLAdditions.h"
-#import "NSExpression+MGLAdditions.h"
+#import "NSExpression+MGLPrivateAdditions.h"
+
+#include <mbgl/style/conversion/property_value.hpp>
@implementation NSCompoundPredicate (MGLAdditions)
@@ -14,60 +18,32 @@
return filters;
}
-- (mbgl::style::Filter)mgl_filter
-{
+@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;
- mbgl::style::Filter subfilter = subpredicate.mgl_filter;
-
- // Convert NOT(!= nil) to NotHasFilter.
- if (subfilter.is<mbgl::style::HasFilter>()) {
- auto hasFilter = subfilter.get<mbgl::style::HasFilter>();
- return mbgl::style::NotHasFilter { .key = hasFilter.key };
- }
-
- // Convert NOT(== nil) to HasFilter.
- if (subfilter.is<mbgl::style::NotHasFilter>()) {
- auto hasFilter = subfilter.get<mbgl::style::NotHasFilter>();
- return mbgl::style::HasFilter { .key = hasFilter.key };
- }
-
- // Convert NOT(IN) or NOT(CONTAINS) to NotInFilter.
- if (subfilter.is<mbgl::style::InFilter>()) {
- auto inFilter = subfilter.get<mbgl::style::InFilter>();
- mbgl::style::NotInFilter notInFilter;
- notInFilter.key = inFilter.key;
- notInFilter.values = inFilter.values;
- return notInFilter;
- }
-
- // Convert NOT(), NOT(AND), NOT(NOT), NOT(==), etc. into NoneFilter.
- mbgl::style::NoneFilter noneFilter;
- if (subfilter.is<mbgl::style::AnyFilter>()) {
- // Flatten NOT(OR).
- noneFilter.filters = subfilter.get<mbgl::style::AnyFilter>().filters;
- } else if (subpredicate) {
- noneFilter.filters = { subfilter };
- }
- return noneFilter;
+ return @[@"!", subpredicate.mgl_jsonExpressionObject];
}
+
case NSAndPredicateType: {
- mbgl::style::AllFilter filter;
- filter.filters = self.mgl_subfilters;
- return filter;
+ NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"all"] arrayByAddingObjectsFromArray:subarrays];
}
+
case NSOrPredicateType: {
- mbgl::style::AnyFilter filter;
- filter.filters = self.mgl_subfilters;
- return filter;
+ NSArray *subarrays = [self.subpredicates valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"any"] arrayByAddingObjectsFromArray:subarrays];
}
}
-
+
[NSException raise:@"Compound predicate type not handled"
format:@""];
- return {};
+ 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..cfdf27aade 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -1,17 +1,200 @@
#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
+typedef NSString *MGLExpressionInterpolationMode NS_TYPED_ENUM;
+
+/**
+ An `NSString` identifying the `linear` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `linear` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeLinear;
+
+/**
+ An `NSString` identifying the `expotential` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `exponential` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeExponential;
+
+/**
+ An `NSString` identifying the `cubic-bezier` interpolation type in an `NSExpression`.
+
+ This attribute corresponds to the `cubic-bezier` value in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-interpolate"><code>interpolate</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+extern MGL_EXPORT const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier;
+
+/**
+ Methods for creating expressions that use Mapbox-specific functionality and for
+ converting to and from the JSON format defined in the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions">Mapbox Style Specification</a>.
+ */
@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;
+#pragma mark Creating Variable Expressions
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-zoom"><code>zoom</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *zoomLevelVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-heatmap-density"><code>heatmap-density</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *heatmapDensityVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#eexpressions-geometry-type"><code>geometry-type</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *geometryTypeVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-id"><code>id</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *featureIdentifierVariableExpression;
+
+/**
+ `NSExpression` variable that corresponds to the
+ <a href="https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions-properties"><code>properties</code></a>
+ expression operator in the Mapbox Style Specification.
+ */
+@property (class, nonatomic, readonly) NSExpression *featureAttributesVariableExpression;
+
+@property (class, nonatomic, readonly) NSExpression *featurePropertiesVariableExpression __attribute__((deprecated("Use -featureAttributesVariableExpression.")));
+
+#pragma mark Creating Conditional Expressions
+
+/**
+ Returns a conditional function expression specifying the string predicate, and
+ expressions for each condition.
+
+ @param conditionPredicate The predicate to get evaluated.
+ @param trueExpression The expression for conditions equal to true.
+ @param falseExpression The expression for conditions equal to false.
+ */
++ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression NS_SWIFT_NAME(init(forMGLConditional:trueExpression:falseExpression:));
+
+#pragma mark Creating Ramp, Scale, and Curve Expressions
+
+/**
+ Returns a step function expression specifying the stepping, from expression
+ and stops.
+
+ @param steppingExpression The stepping expression.
+ @param minimumExpression The expression which could be a constant or function expression.
+ @param stops The stops must be an `NSDictionary` constant `NSExpression`.
+ */
++ (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression*)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLStepping:from:stops:));
+
+/**
+ Returns an interpolated function expression specifying the function operator, curve type,
+ parameters and steps.
+
+ @param inputExpression The interpolating expression input.
+ @param curveType The curve type could be `MGLExpressionInterpolationModeLinear`,
+ `MGLExpressionInterpolationModeExponential` and
+ `MGLExpressionInterpolationModeCubicBezier`.
+ @param parameters The parameters expression.
+ @param stops The stops expression.
+ */
++ (instancetype)mgl_expressionForInterpolatingExpression:(nonnull NSExpression*)inputExpression withCurveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters stops:(nonnull NSExpression*)stops NS_SWIFT_NAME(init(forMGLInterpolating:curveType:parameters:stops:));
+
+/**
+ Returns a match function expression specifying the input, matching values,
+ and default value.
+
+ @param inputExpression The matching expression.
+ @param matchedExpressions The matched values expression dictionary must be condition : value.
+ @param defaultExpression The defaultValue expression to be used in case there is no match.
+ */
++ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)inputExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression NS_SWIFT_NAME(init(forMGLMatchingKey:in:default:));
+
+#pragma mark Concatenating String Expressions
+
+/**
+ Returns a constant expression appending the passed expression.
+
+ @note Both the receiver and the given expression must be an `NSString` constant
+ expression type; otherwise, an exception is rised.
+
+ @param expression The expression to append to the receiver.
+ */
+- (instancetype)mgl_expressionByAppendingExpression:(nonnull NSExpression *)expression NS_SWIFT_NAME(mgl_appending(_:));
+
+#pragma mark Converting JSON Expressions
+
+/**
+ 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
+ “[Information for Style Authors](../for-style-authors.html#setting-attribute-values)”
+ 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)expressionWithMGLJSONObject:(id)object NS_SWIFT_NAME(init(mglJSONObject:));
+
+/**
+ 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
+ “[Information for Style Authors](../for-style-authors.html#setting-attribute-values)”
+ 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;
+
+#pragma mark Localizing the Expression
+
+/**
+ Returns a copy of the receiver localized into the given locale.
+
+ This method assumes the receiver refers to the feature attributes that are
+ available in vector tiles supplied by the
+ <a href="https://www.mapbox.com/vector-tiles/mapbox-streets-v7/#overview">Mapbox Streets source</a>.
+ On iOS, the user can set the system’s preferred language in Settings, General
+ Settings, Language & Region. On macOS, the user can set the system’s preferred
+ language in the Language & Region pane of System Preferences.
+
+ @param locale The locale into which labels should be localized. To use the
+ system’s preferred language, if supported, specify `nil`. To use the local
+ language, specify a locale with the identifier `mul`.
+ */
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale;
@end
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index a7759cda9d..a42a2d276e 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -1,13 +1,249 @@
-#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"
+#import "MGLVectorTileSource_Private.h"
-@implementation NSExpression (MGLAdditions)
+#import <objc/runtime.h>
+
+#import <mbgl/style/expression/expression.hpp>
+
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeLinear = @"linear";
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeExponential = @"exponential";
+const MGLExpressionInterpolationMode MGLExpressionInterpolationModeCubicBezier = @"cubic-bezier";
+
+@interface MGLAftermarketExpressionInstaller: NSObject
+@end
+
+@implementation MGLAftermarketExpressionInstaller
+
++ (void)load {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ [self installFunctions];
+ });
+}
+
+/**
+ Adds to NSExpression’s built-in repertoire of functions.
+ */
++ (void)installFunctions {
+ Class MGLAftermarketExpressionInstaller = [self class];
+
+ // NSExpression’s built-in functions are backed by class methods on a
+ // private class, so use a function expression to get at the class.
+ // http://funwithobjc.tumblr.com/post/2922267976/using-custom-functions-with-nsexpression
+ NSExpression *functionExpression = [NSExpression expressionWithFormat:@"sum({})"];
+ NSString *className = NSStringFromClass([functionExpression.operand.constantValue class]);
+
+ // Effectively categorize the class with some extra class methods.
+ Class NSPredicateUtilities = objc_getMetaClass(className.UTF8String);
+#pragma clang push
+#pragma clang diagnostic ignored "-Wundeclared-selector"
+ #define INSTALL_METHOD(sel) \
+ { \
+ Method method = class_getInstanceMethod(MGLAftermarketExpressionInstaller, @selector(sel)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel), method_getImplementation(method), method_getTypeEncoding(method)); \
+ }
+ #define INSTALL_CONTROL_STRUCTURE(sel) \
+ { \
+ Method method = class_getInstanceMethod(MGLAftermarketExpressionInstaller, @selector(sel:)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel), method_getImplementation(method), method_getTypeEncoding(method)); \
+ class_addMethod(NSPredicateUtilities, @selector(sel:), method_getImplementation(method), method_getTypeEncoding(method)); \
+ }
+
+ // Install method-like functions, taking the number of arguments implied by
+ // the selector name.
+ INSTALL_METHOD(mgl_join:);
+ INSTALL_METHOD(mgl_round:);
+ INSTALL_METHOD(mgl_interpolate:withCurveType:parameters:stops:);
+ INSTALL_METHOD(mgl_step:from:stops:);
+ INSTALL_METHOD(mgl_coalesce:);
+ INSTALL_METHOD(mgl_does:have:);
+ INSTALL_METHOD(mgl_acos:);
+ INSTALL_METHOD(mgl_cos:);
+ INSTALL_METHOD(mgl_asin:);
+ INSTALL_METHOD(mgl_sin:);
+ INSTALL_METHOD(mgl_atan:);
+ INSTALL_METHOD(mgl_tan:);
+ INSTALL_METHOD(mgl_log2:);
+
+ // Install functions that resemble control structures, taking arbitrary
+ // numbers of arguments. Vararg aftermarket functions need to be declared
+ // with an explicit and implicit first argument.
+ INSTALL_CONTROL_STRUCTURE(MGL_LET);
+ INSTALL_CONTROL_STRUCTURE(MGL_MATCH);
+ INSTALL_CONTROL_STRUCTURE(MGL_IF);
+ INSTALL_CONTROL_STRUCTURE(MGL_FUNCTION);
+
+ #undef INSTALL_AFTERMARKET_FN
+#pragma clang pop
+}
+
+/**
+ Joins the given components into a single string by concatenating each component
+ in order.
+ */
+- (NSString *)mgl_join:(NSArray<NSString *> *)components {
+ return [components componentsJoinedByString:@""];
+}
+
+/**
+ Rounds the given number to the nearest integer. If the number is halfway
+ between two integers, this method rounds it away from zero.
+ */
+- (NSNumber *)mgl_round:(NSNumber *)number {
+ return @(round(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse cosine.
+ */
+- (NSNumber *)mgl_acos:(NSNumber *)number {
+ return @(acos(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the cosine.
+ */
+- (NSNumber *)mgl_cos:(NSNumber *)number {
+ return @(cos(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse sine.
+ */
+- (NSNumber *)mgl_asin:(NSNumber *)number {
+ return @(asin(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the sine.
+ */
+- (NSNumber *)mgl_sin:(NSNumber *)number {
+ return @(sin(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the inverse tangent.
+ */
+- (NSNumber *)mgl_atan:(NSNumber *)number {
+ return @(atan(number.doubleValue));
+}
+
+/**
+ Computes the principal value of the tangent.
+ */
+- (NSNumber *)mgl_tan:(NSNumber *)number {
+ return @(tan(number.doubleValue));
+}
+
+/**
+ Computes the logarithm base two of the value.
+ */
+- (NSNumber *)mgl_log2:(NSNumber *)number {
+ return @(log2(number.doubleValue));
+}
+
+/**
+ A placeholder for a method that evaluates an interpolation expression.
+ */
+- (id)mgl_interpolate:(id)inputExpression withCurveType:(NSString *)curveType parameters:(NSDictionary *)params stops:(NSDictionary *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Interpolation expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates a step expression.
+ */
+- (id)mgl_step:(id)inputExpression from:(id)minimumExpression stops:(NSDictionary *)stops {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Step expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates a coalesce expression.
+ */
+- (id)mgl_coalesce:(NSArray<NSExpression *> *)elements {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Coalesce expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ Returns a Boolean value indicating whether the object has a value for the given
+ key.
+ */
+- (BOOL)mgl_does:(id)object have:(NSString *)key {
+ return [object valueForKey:key] != nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression based on an arbitrary
+ number of variable names and assigned expressions.
+ */
+- (id)MGL_LET:(NSString *)firstVariableName, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression and returns the matching element.
+ */
+- (id)MGL_MATCH:(id)firstCondition, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+/**
+ A placeholder for a method that evaluates an expression and returns the matching element.
+ */
+- (id)MGL_IF:(id)firstCondition, ... {
+ va_list argumentList;
+ va_start(argumentList, firstCondition);
+
+ for (id eachExpression = firstCondition; eachExpression; eachExpression = va_arg(argumentList, id)) {
+ if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) {
+ id valueExpression = va_arg(argumentList, id);
+ if ([eachExpression evaluateWithObject:nil]) {
+ return valueExpression;
+ }
+ } else {
+ return eachExpression;
+ }
+ }
+ va_end(argumentList);
+
+ return nil;
+}
+
+
+/**
+ A placeholder for a catch-all method that evaluates an arbitrary number of
+ arguments as an expression according to the Mapbox Style Specification’s
+ expression language.
+ */
+- (id)MGL_FUNCTION:(id)firstArgument, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Mapbox GL function expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+@end
+
+@implementation NSExpression (MGLPrivateAdditions)
- (std::vector<mbgl::Value>)mgl_aggregateMBGLValue {
if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
@@ -153,4 +389,1081 @@
return {};
}
+// Selectors of functions that can contain tokens in arguments.
+static NSArray * const MGLTokenizedFunctions = @[
+ @"mgl_interpolateWithCurveType:parameters:stops:",
+ @"mgl_interpolate:withCurveType:parameters:stops:",
+ @"mgl_stepWithMinimum:stops:",
+ @"mgl_step:from:stops:",
+];
+
+/**
+ Returns a copy of the given collection with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original collection.
+ */
+NSArray<NSExpression *> *MGLCollectionByReplacingTokensWithKeyPaths(NSArray<NSExpression *> *collection) {
+ __block NSMutableArray *upgradedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *upgradedItem = item.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedItem != item) {
+ if (!upgradedCollection) {
+ upgradedCollection = [collection mutableCopy];
+ }
+ upgradedCollection[idx] = upgradedItem;
+ }
+ }];
+ return upgradedCollection ?: collection;
+};
+
+/**
+ Returns a copy of the given stop dictionary with tokens replaced by key path
+ expressions.
+
+ If no replacements take place, this method returns the original stop
+ dictionary.
+ */
+NSDictionary<NSNumber *, NSExpression *> *MGLStopDictionaryByReplacingTokensWithKeyPaths(NSDictionary<NSNumber *, NSExpression *> *stops) {
+ __block NSMutableDictionary *upgradedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *upgradedValue = value.mgl_expressionByReplacingTokensWithKeyPaths;
+ if (upgradedValue != value) {
+ if (!upgradedStops) {
+ upgradedStops = [stops mutableCopy];
+ }
+ upgradedStops[zoomLevel] = upgradedValue;
+ }
+ }];
+ return upgradedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSString *constantValue = self.constantValue;
+ if ([constantValue isKindOfClass:[NSString class]] &&
+ [constantValue containsString:@"{"] && [constantValue containsString:@"}"]) {
+ NSMutableArray *components = [NSMutableArray array];
+ NSScanner *scanner = [NSScanner scannerWithString:constantValue];
+ scanner.charactersToBeSkipped = nil;
+ while (!scanner.isAtEnd) {
+ NSString *string;
+ if ([scanner scanUpToString:@"{" intoString:&string]) {
+ [components addObject:[NSExpression expressionForConstantValue:string]];
+ }
+
+ NSString *token;
+ if ([scanner scanString:@"{" intoString:NULL]
+ && [scanner scanUpToString:@"}" intoString:&token]
+ && [scanner scanString:@"}" intoString:NULL]) {
+ [components addObject:[NSExpression expressionForKeyPath:token]];
+ }
+ }
+ if (components.count == 1) {
+ return components.firstObject;
+ }
+ return [NSExpression expressionForFunction:@"mgl_join:"
+ arguments:@[[NSExpression expressionForAggregate:components]]];
+ }
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLStopDictionaryByReplacingTokensWithKeyPaths(stops);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ if ([MGLTokenizedFunctions containsObject:self.function]) {
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLCollectionByReplacingTokensWithKeyPaths(arguments);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:self.operand selectorName:self.function arguments:localizedArguments];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
+@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"];
+}
+
+- (id)mgl_coalesce {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Coalesce expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+@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;
+}
+
+- (id)mgl_has:(id)element {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Has expressions lack underlying Objective-C implementations."];
+ return nil;
+
+}
+
+@end
+
+@implementation NSExpression (MGLExpressionAdditions)
+
+- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context {
+ [NSException raise:NSInternalInconsistencyException
+ format:@"Assignment expressions lack underlying Objective-C implementations."];
+ return self;
+}
+
+- (id)mgl_has:(id)element {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Has expressions lack underlying Objective-C implementations."];
+ return nil;
+}
+
+@end
+
+@implementation NSExpression (MGLAdditions)
+
++ (NSExpression *)zoomLevelVariableExpression {
+ return [NSExpression expressionForVariable:@"zoomLevel"];
+}
+
++ (NSExpression *)heatmapDensityVariableExpression {
+ return [NSExpression expressionForVariable:@"heatmapDensity"];
+}
+
++ (NSExpression *)geometryTypeVariableExpression {
+ return [NSExpression expressionForVariable:@"geometryType"];
+}
+
++ (NSExpression *)featureIdentifierVariableExpression {
+ return [NSExpression expressionForVariable:@"featureIdentifier"];
+}
+
++ (NSExpression *)featureAttributesVariableExpression {
+ return [NSExpression expressionForVariable:@"featureAttributes"];
+}
+
++ (NSExpression *)featurePropertiesVariableExpression {
+ return [self featureAttributesVariableExpression];
+}
+
++ (instancetype)mgl_expressionForConditional:(nonnull NSPredicate *)conditionPredicate trueExpression:(nonnull NSExpression *)trueExpression falseExpresssion:(nonnull NSExpression *)falseExpression {
+ return [NSExpression expressionForConditional:conditionPredicate trueExpression:trueExpression falseExpression:falseExpression];
+}
+
++ (instancetype)mgl_expressionForSteppingExpression:(nonnull NSExpression *)steppingExpression fromExpression:(nonnull NSExpression *)minimumExpression stops:(nonnull NSExpression *)stops {
+ return [NSExpression expressionForFunction:@"mgl_step:from:stops:"
+ arguments:@[steppingExpression, minimumExpression, stops]];
+}
+
++ (instancetype)mgl_expressionForInterpolatingExpression:(nonnull NSExpression *)inputExpression withCurveType:(nonnull MGLExpressionInterpolationMode)curveType parameters:(nullable NSExpression *)parameters stops:(nonnull NSExpression *)stops {
+ NSExpression *sanitizeParams = parameters ? parameters : [NSExpression expressionForConstantValue:nil];
+ return [NSExpression expressionForFunction:@"mgl_interpolate:withCurveType:parameters:stops:"
+ arguments:@[inputExpression, [NSExpression expressionForConstantValue:curveType], sanitizeParams, stops]];
+}
+
++ (instancetype)mgl_expressionForMatchingExpression:(nonnull NSExpression *)inputExpression inDictionary:(nonnull NSDictionary<NSExpression *, NSExpression *> *)matchedExpressions defaultExpression:(nonnull NSExpression *)defaultExpression {
+ NSMutableArray *optionsArray = [NSMutableArray arrayWithObjects:inputExpression, nil];
+
+ NSEnumerator *matchEnumerator = matchedExpressions.keyEnumerator;
+ while (NSExpression *key = matchEnumerator.nextObject) {
+ [optionsArray addObject:key];
+ [optionsArray addObject:[matchedExpressions objectForKey:key]];
+ }
+
+ [optionsArray addObject:defaultExpression];
+ return [NSExpression expressionForFunction:@"MGL_MATCH"
+ arguments:optionsArray];
+}
+
+- (instancetype)mgl_expressionByAppendingExpression:(nonnull NSExpression *)expression {
+ NSExpression *subexpression = [NSExpression expressionForAggregate:@[self, expression]];
+ return [NSExpression expressionForFunction:@"mgl_join:" arguments:@[subexpression]];
+}
+
+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 expressionWithMGLJSONObject:object];
+ [subexpressions addObject:expression];
+ }
+ return subexpressions;
+}
+
++ (instancetype)expressionWithMGLJSONObject:(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:",
+ @"abs": @"abs:",
+ @"round": @"mgl_round:",
+ @"acos" : @"mgl_acos:",
+ @"cos" : @"mgl_cos:",
+ @"asin" : @"mgl_asin:",
+ @"sin" : @"mgl_sin:",
+ @"atan" : @"mgl_atan:",
+ @"tan" : @"mgl_tan:",
+ @"log2" : @"mgl_log2:",
+ @"floor": @"floor:",
+ @"ceil": @"ceiling:",
+ @"^": @"raise:toPower:",
+ @"upcase": @"uppercase:",
+ @"downcase": @"lowercase:",
+ @"let": @"MGL_LET",
+ };
+ });
+
+ 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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"to-boolean"]) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionForFunction:operand selectorName:@"boolValue" arguments:@[]];
+ } else if ([op isEqualToString:@"to-number"] || [op isEqualToString:@"number"]) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ if (argumentObjects.count == 1) {
+ return [NSExpression expressionWithFormat:@"CAST(%@, 'NSNumber')", operand];
+ }
+ 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"] || [op isEqualToString:@"string"]) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionWithFormat:@"CAST(%@, 'NSString')", operand];
+ } else if ([op isEqualToString:@"get"]) {
+ if (argumentObjects.count == 2) {
+ NSExpression *operand = [NSExpression expressionWithMGLJSONObject:argumentObjects.lastObject];
+ if ([argumentObjects.firstObject isKindOfClass:[NSString class]]) {
+ return [NSExpression expressionWithFormat:@"%@.%K", operand, argumentObjects.firstObject];
+ }
+ NSExpression *key = [NSExpression expressionWithMGLJSONObject:argumentObjects.firstObject];
+ return [NSExpression expressionWithFormat:@"%@.%@", operand, key];
+ }
+ return [NSExpression expressionForKeyPath:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"length"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSString *function = @"count:";
+ if ([subexpressions.firstObject expressionType] == NSConstantValueExpressionType
+ && [[subexpressions.firstObject constantValue] isKindOfClass:[NSString class]]) {
+ function = @"length:";
+ }
+ return [NSExpression expressionForFunction:function arguments:@[subexpressions.firstObject]];
+ } else if ([op isEqualToString:@"rgb"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression mgl_expressionForRGBComponents:subexpressions];
+ } else if ([op isEqualToString:@"rgba"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ return [NSExpression mgl_expressionForRGBAComponents:subexpressions];
+ } 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 *subexpression = [NSExpression expressionForAggregate:subexpressions];
+ return [NSExpression expressionForFunction:@"mgl_join:" arguments:@[subexpression]];
+ } else if ([op isEqualToString:@"at"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *index = subexpressions.firstObject;
+ NSExpression *operand = subexpressions[1];
+ return [NSExpression expressionForFunction:@"objectFrom:withIndex:" arguments:@[operand, index]];
+ } else if ([op isEqualToString:@"has"]) {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(argumentObjects);
+ NSExpression *operand = argumentObjects.count > 1 ? subexpressions[1] : [NSExpression expressionForEvaluatedObject];
+ NSExpression *key = subexpressions.firstObject;
+ return [NSExpression expressionForFunction:@"mgl_does:have:" arguments:@[operand, key]];
+ } else if ([op isEqualToString:@"interpolate"]) {
+ NSArray *interpolationOptions = argumentObjects.firstObject;
+ NSString *curveType = interpolationOptions.firstObject;
+ NSExpression *curveTypeExpression = [NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:curveParameters];
+ argumentObjects = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSExpression *inputExpression = [NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:valueExpression];
+ }
+ NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
+ return [NSExpression expressionForFunction:@"mgl_interpolate:withCurveType:parameters:stops:"
+ arguments:@[inputExpression, curveTypeExpression, curveParameterExpression, stopExpression]];
+ } else if ([op isEqualToString:@"step"]) {
+ NSExpression *inputExpression = [NSExpression expressionWithMGLJSONObject:argumentObjects[0]];
+ NSArray *stopExpressions = [argumentObjects subarrayWithRange:NSMakeRange(1, argumentObjects.count - 1)];
+ NSExpression *minimum;
+ if (stopExpressions.count % 2) {
+ minimum = [NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:valueExpression];
+ } else {
+ minimum = [NSExpression expressionWithMGLJSONObject:valueExpression];
+ }
+ }
+ NSExpression *stopExpression = [NSExpression expressionForConstantValue:stops];
+ return [NSExpression expressionForFunction:@"mgl_step:from:stops:"
+ arguments:@[inputExpression, minimum, stopExpression]];
+ } else if ([op isEqualToString:@"zoom"]) {
+ return NSExpression.zoomLevelVariableExpression;
+ } else if ([op isEqualToString:@"heatmap-density"]) {
+ return NSExpression.heatmapDensityVariableExpression;
+ } else if ([op isEqualToString:@"geometry-type"]) {
+ return NSExpression.geometryTypeVariableExpression;
+ } else if ([op isEqualToString:@"id"]) {
+ return NSExpression.featureIdentifierVariableExpression;
+ } else if ([op isEqualToString:@"properties"]) {
+ return NSExpression.featureAttributesVariableExpression;
+ } else if ([op isEqualToString:@"var"]) {
+ return [NSExpression expressionForVariable:argumentObjects.firstObject];
+ } else if ([op isEqualToString:@"case"]) {
+ NSMutableArray *arguments = [NSMutableArray array];
+
+ for (NSUInteger index = 0; index < argumentObjects.count; index++) {
+ if (index % 2 == 0 && index != argumentObjects.count - 1) {
+ NSPredicate *predicate = [NSPredicate mgl_predicateWithJSONObject:argumentObjects[index]];
+ NSExpression *argument = [NSExpression expressionForConstantValue:predicate];
+ [arguments addObject:argument];
+ } else {
+ [arguments addObject:[NSExpression expressionWithMGLJSONObject:argumentObjects[index]]];
+ }
+ }
+
+ if (arguments.count == 3) {
+ NSPredicate *conditional = [arguments.firstObject constantValue];
+ return [NSExpression expressionForConditional:conditional trueExpression:arguments[1] falseExpression:arguments[2]];
+ }
+ return [NSExpression expressionForFunction:@"MGL_IF" arguments:arguments];
+ } else if ([op isEqualToString:@"match"]) {
+ NSMutableArray *optionsArray = [NSMutableArray array];
+ NSEnumerator *optionsEnumerator = argumentObjects.objectEnumerator;
+ while (id object = optionsEnumerator.nextObject) {
+ NSExpression *option = [NSExpression expressionWithMGLJSONObject:object];
+ [optionsArray addObject:option];
+ }
+
+ return [NSExpression expressionForFunction:@"MGL_MATCH"
+ arguments:optionsArray];
+ } else if ([op isEqualToString:@"coalesce"]) {
+ NSMutableArray *expressions = [NSMutableArray array];
+ for (id operand in argumentObjects) {
+ [expressions addObject:[NSExpression expressionWithMGLJSONObject:operand]];
+ }
+
+ return [NSExpression expressionWithFormat:@"mgl_coalesce(%@)", expressions];
+ } else {
+ NSArray *subexpressions = MGLSubexpressionsWithJSONObjects(array);
+ return [NSExpression expressionForFunction:@"MGL_FUNCTION" arguments:subexpressions];
+ }
+ }
+
+ [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:": @"^",
+ @"ceiling:": @"ceil",
+ @"abs:": @"abs",
+ @"floor:": @"floor",
+ @"uppercase:": @"upcase",
+ @"lowercase:": @"downcase",
+ @"length:": @"length",
+ @"mgl_round:": @"round",
+ @"mgl_acos:" : @"acos",
+ @"mgl_cos:" : @"cos",
+ @"mgl_asin:" : @"asin",
+ @"mgl_sin:" : @"sin",
+ @"mgl_atan:" : @"atan",
+ @"mgl_tan:" : @"tan",
+ @"mgl_log2:" : @"log2",
+ // Vararg aftermarket expressions need to be declared with an explicit and implicit first argument.
+ @"MGL_LET": @"let",
+ @"MGL_LET:": @"let",
+ };
+ });
+
+ switch (self.expressionType) {
+ case NSVariableExpressionType: {
+ if ([self.variable isEqualToString:@"heatmapDensity"]) {
+ return @[@"heatmap-density"];
+ }
+ if ([self.variable isEqualToString:@"zoomLevel"]) {
+ return @[@"zoom"];
+ }
+ if ([self.variable isEqualToString:@"geometryType"]) {
+ return @[@"geometry-type"];
+ }
+ if ([self.variable isEqualToString:@"featureIdentifier"]) {
+ return @[@"id"];
+ }
+ if ([self.variable isEqualToString:@"featureAttributes"]) {
+ return @[@"properties"];
+ }
+ 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:@"valueForKey:"] || [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:@"trunc:"]) {
+ return [NSExpression expressionWithFormat:@"%@ - modulus:by:(%@, 1)",
+ self.arguments.firstObject, self.arguments.firstObject].mgl_jsonExpressionObject;
+ } else if ([function isEqualToString:@"mgl_join:"]) {
+ NSArray *arguments = [self.arguments.firstObject.collection valueForKeyPath:@"mgl_jsonExpressionObject"];
+ return [@[@"concat"] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"stringByAppendingString:"]) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[@"concat", self.operand.mgl_jsonExpressionObject] arrayByAddingObjectsFromArray:arguments];
+ } else if ([function isEqualToString:@"objectFrom:withIndex:"]) {
+ return @[@"at", self.arguments[1].mgl_jsonExpressionObject, self.arguments[0].mgl_jsonExpressionObject];
+ } else if ([function isEqualToString:@"boolValue"]) {
+ return @[@"to-boolean", self.operand.mgl_jsonExpressionObject];
+ } else if ([function isEqualToString:@"mgl_number"] ||
+ [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_does:have:"] ||
+ [function isEqualToString:@"mgl_has:"]) {
+ return self.mgl_jsonHasExpressionObject;
+ } else if ([function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"]
+ || [function isEqualToString:@"mgl_interpolateWithCurveType:parameters:stops:"]) {
+ return self.mgl_jsonInterpolationExpressionObject;
+ } else if ([function isEqualToString:@"mgl_step:from:stops:"]
+ || [function isEqualToString:@"mgl_stepWithMinimum:stops:"]) {
+ return self.mgl_jsonStepExpressionObject;
+ } 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:@"MGL_IF"] ||
+ [function isEqualToString:@"mgl_if:"]) {
+ return self.mgl_jsonIfExpressionObject;
+ } else if ([function isEqualToString:@"MGL_MATCH"] ||
+ [function isEqualToString:@"mgl_match:"]) {
+ return self.mgl_jsonMatchExpressionObject;
+ } else if ([function isEqualToString:@"mgl_coalesce:"] ||
+ [function isEqualToString:@"mgl_coalesce"]) {
+
+ return self.mgl_jsonCoalesceExpressionObject;
+ } else if ([function isEqualToString:@"castObject:toType:"]) {
+ id object = self.arguments.firstObject.mgl_jsonExpressionObject;
+ NSString *type = self.arguments[1].mgl_jsonExpressionObject;
+ if ([type isEqualToString:@"NSString"]) {
+ return @[@"to-string", object];
+ } else if ([type isEqualToString:@"NSNumber"]) {
+ return @[@"to-number", object];
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Casting expression to %@ not yet implemented.", type];
+ } else if ([function isEqualToString:@"MGL_FUNCTION"]) {
+ return self.arguments.mgl_jsonExpressionObject;
+ } else if (op == [MGLColor class] && [function isEqualToString:@"colorWithRed:green:blue:alpha:"]) {
+ NSArray *arguments = self.arguments.mgl_jsonExpressionObject;
+ return [@[@"rgba"] arrayByAddingObjectsFromArray:arguments];
+ } 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, nil];
+
+ if (self.trueExpression.expressionType == NSConditionalExpressionType) {
+ // Fold nested conditionals into a single case expression.
+ NSArray *trueArguments = self.trueExpression.mgl_jsonExpressionObject;
+ trueArguments = [trueArguments subarrayWithRange:NSMakeRange(1, trueArguments.count - 1)];
+ [arguments addObjectsFromArray:trueArguments];
+ } else {
+ [arguments addObject:self.trueExpression.mgl_jsonExpressionObject];
+ }
+
+ if (self.falseExpression.expressionType == NSConditionalExpressionType) {
+ // Fold nested conditionals into a single case expression.
+ NSArray *falseArguments = self.falseExpression.mgl_jsonExpressionObject;
+ 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;
+}
+
+- (id)mgl_jsonInterpolationExpressionObject {
+ NSUInteger expectedArgumentCount = [self.function componentsSeparatedByString:@":"].count - 1;
+ if (self.arguments.count < expectedArgumentCount) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"Too few arguments to ‘%@’ function; expected %lu arguments.",
+ self.function, expectedArgumentCount];
+ } else if (self.arguments.count > expectedArgumentCount) {
+ [NSException raise:NSInvalidArgumentException format:
+ @"%lu unexpected arguments to ‘%@’ function; expected %lu arguments.",
+ self.arguments.count - expectedArgumentCount, self.function, expectedArgumentCount];
+ }
+
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_interpolate:withCurveType:parameters:stops:"];
+ NSUInteger curveTypeIndex = isAftermarketFunction ? 1 : 0;
+ NSString *curveType = self.arguments[curveTypeIndex].constantValue;
+ NSMutableArray *interpolationArray = [NSMutableArray arrayWithObject:curveType];
+ if ([curveType isEqualToString:@"exponential"]) {
+ id base = [self.arguments[curveTypeIndex + 1] mgl_jsonExpressionObject];
+ [interpolationArray addObject:base];
+ } else if ([curveType isEqualToString:@"cubic-bezier"]) {
+ NSArray *controlPoints = [self.arguments[curveTypeIndex + 1].collection mgl_jsonExpressionObject];
+ [interpolationArray addObjectsFromArray:controlPoints];
+ }
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"interpolate", interpolationArray, nil];
+ [expressionObject addObject:(isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[curveTypeIndex + 2].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+}
+
+- (id)mgl_jsonStepExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_step:from:stops:"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+ id minimum = self.arguments[minimumIndex].mgl_jsonExpressionObject;
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"step", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, minimum, nil];
+ NSDictionary<NSNumber *, NSExpression *> *stops = self.arguments[minimumIndex + 1].constantValue;
+ for (NSNumber *key in [stops.allKeys sortedArrayUsingSelector:@selector(compare:)]) {
+ [expressionObject addObject:key];
+ [expressionObject addObject:[stops[key] mgl_jsonExpressionObject]];
+ }
+ return expressionObject;
+}
+
+- (id)mgl_jsonMatchExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"MGL_MATCH"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"match", (isAftermarketFunction ? self.arguments.firstObject : self.operand).mgl_jsonExpressionObject, nil];
+ NSArray<NSExpression *> *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue;
+
+ for (NSUInteger index = minimumIndex; index < arguments.count; index++) {
+ [expressionObject addObject:arguments[index].mgl_jsonExpressionObject];
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonIfExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"MGL_IF"];
+ NSUInteger minimumIndex = isAftermarketFunction ? 1 : 0;
+ NSExpression *firstCondition;
+ id condition;
+
+ if (isAftermarketFunction) {
+ firstCondition = self.arguments.firstObject;
+ } else {
+ firstCondition = self.operand;
+ }
+
+ if ([firstCondition respondsToSelector:@selector(constantValue)] && [firstCondition.constantValue isKindOfClass:[NSComparisonPredicate class]]) {
+ NSPredicate *predicate = (NSPredicate *)firstCondition.constantValue;
+ condition = predicate.mgl_jsonExpressionObject;
+ } else {
+ condition = firstCondition.mgl_jsonExpressionObject;
+ }
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"case", condition, nil];
+ NSArray<NSExpression *> *arguments = isAftermarketFunction ? self.arguments : self.arguments[minimumIndex].constantValue;
+
+ for (NSUInteger index = minimumIndex; index < arguments.count; index++) {
+ if ([arguments[index] respondsToSelector:@selector(constantValue)] && [arguments[index].constantValue isKindOfClass:[NSComparisonPredicate class]]) {
+ NSPredicate *predicate = (NSPredicate *)arguments[index].constantValue;
+ [expressionObject addObject:predicate.mgl_jsonExpressionObject];
+ } else {
+ [expressionObject addObject:arguments[index].mgl_jsonExpressionObject];
+ }
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonCoalesceExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_coalesce:"];
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"coalesce", nil];
+
+ for (NSExpression *expression in (isAftermarketFunction ? self.arguments.firstObject : self.operand).constantValue) {
+ [expressionObject addObject:[expression mgl_jsonExpressionObject]];
+ }
+
+ return expressionObject;
+}
+
+- (id)mgl_jsonHasExpressionObject {
+ BOOL isAftermarketFunction = [self.function isEqualToString:@"mgl_does:have:"];
+ NSExpression *operand = isAftermarketFunction ? self.arguments[0] : self.operand;
+ NSExpression *key = self.arguments[isAftermarketFunction ? 1 : 0];
+
+ NSMutableArray *expressionObject = [NSMutableArray arrayWithObjects:@"has", key.mgl_jsonExpressionObject, nil];
+ if (operand.expressionType != NSEvaluatedObjectExpressionType) {
+ [expressionObject addObject:operand.mgl_jsonExpressionObject];
+ }
+ return expressionObject;
+}
+
+#pragma mark Localization
+
+/**
+ Returns a localized copy of the given collection.
+
+ If no localization takes place, this method returns the original collection.
+ */
+NSArray<NSExpression *> *MGLLocalizedCollection(NSArray<NSExpression *> *collection, NSLocale * _Nullable locale) {
+ __block NSMutableArray *localizedCollection;
+ [collection enumerateObjectsUsingBlock:^(NSExpression * _Nonnull item, NSUInteger idx, BOOL * _Nonnull stop) {
+ NSExpression *localizedItem = [item mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedItem != item) {
+ if (!localizedCollection) {
+ localizedCollection = [collection mutableCopy];
+ }
+ localizedCollection[idx] = localizedItem;
+ }
+ }];
+ return localizedCollection ?: collection;
+};
+
+/**
+ Returns a localized copy of the given stop dictionary.
+
+ If no localization takes place, this method returns the original stop
+ dictionary.
+ */
+NSDictionary<NSNumber *, NSExpression *> *MGLLocalizedStopDictionary(NSDictionary<NSNumber *, NSExpression *> *stops, NSLocale * _Nullable locale) {
+ __block NSMutableDictionary *localizedStops;
+ [stops enumerateKeysAndObjectsUsingBlock:^(id _Nonnull zoomLevel, NSExpression * _Nonnull value, BOOL * _Nonnull stop) {
+ if (![value isKindOfClass:[NSExpression class]]) {
+ value = [NSExpression expressionForConstantValue:value];
+ }
+ NSExpression *localizedValue = [value mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedValue != value) {
+ if (!localizedStops) {
+ localizedStops = [stops mutableCopy];
+ }
+ localizedStops[zoomLevel] = localizedValue;
+ }
+ }];
+ return localizedStops ?: stops;
+};
+
+- (NSExpression *)mgl_expressionLocalizedIntoLocale:(nullable NSLocale *)locale {
+ switch (self.expressionType) {
+ case NSConstantValueExpressionType: {
+ NSDictionary *stops = self.constantValue;
+ if ([stops isKindOfClass:[NSDictionary class]]) {
+ NSDictionary *localizedStops = MGLLocalizedStopDictionary(stops, locale);
+ if (localizedStops != stops) {
+ return [NSExpression expressionForConstantValue:localizedStops];
+ }
+ }
+ return self;
+ }
+
+ case NSKeyPathExpressionType: {
+ if ([self.keyPath isEqualToString:@"name"] || [self.keyPath hasPrefix:@"name_"]) {
+ NSString *localizedKeyPath = @"name";
+ if (![locale.localeIdentifier isEqualToString:@"mul"]) {
+ NSArray *preferences = locale ? @[locale.localeIdentifier] : [NSLocale preferredLanguages];
+ NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences];
+ if (preferredLanguage) {
+ localizedKeyPath = [NSString stringWithFormat:@"name_%@", preferredLanguage];
+ }
+ }
+ return [NSExpression expressionForKeyPath:localizedKeyPath];
+ }
+ return self;
+ }
+
+ case NSFunctionExpressionType: {
+ NSExpression *operand = self.operand;
+ NSExpression *localizedOperand = [operand mgl_expressionLocalizedIntoLocale:locale];
+
+ NSArray *arguments = self.arguments;
+ NSArray *localizedArguments = MGLLocalizedCollection(arguments, locale);
+ if (localizedArguments != arguments) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:localizedArguments];
+ }
+ if (localizedOperand != operand) {
+ return [NSExpression expressionForFunction:localizedOperand
+ selectorName:self.function
+ arguments:self.arguments];
+ }
+ return self;
+ }
+
+ case NSConditionalExpressionType: {
+ NSExpression *trueExpression = self.trueExpression;
+ NSExpression *localizedTrueExpression = [trueExpression mgl_expressionLocalizedIntoLocale:locale];
+ NSExpression *falseExpression = self.falseExpression;
+ NSExpression *localizedFalseExpression = [falseExpression mgl_expressionLocalizedIntoLocale:locale];
+ if (localizedTrueExpression != trueExpression || localizedFalseExpression != falseExpression) {
+ return [NSExpression expressionForConditional:self.predicate
+ trueExpression:localizedTrueExpression
+ falseExpression:localizedFalseExpression];
+ }
+ return self;
+ }
+
+ case NSAggregateExpressionType: {
+ NSArray *collection = self.collection;
+ if ([collection isKindOfClass:[NSArray class]]) {
+ NSArray *localizedCollection = MGLLocalizedCollection(collection, locale);
+ if (localizedCollection != collection) {
+ return [NSExpression expressionForAggregate:localizedCollection];
+ }
+ }
+ return self;
+ }
+
+ default:
+ return self;
+ }
+}
+
@end
diff --git a/platform/darwin/src/NSExpression+MGLPrivateAdditions.h b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
new file mode 100644
index 0000000000..a1948f9e45
--- /dev/null
+++ b/platform/darwin/src/NSExpression+MGLPrivateAdditions.h
@@ -0,0 +1,89 @@
+#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;
+
+/**
+ Returns a copy of the receiver with tokens replaced by key path expressions.
+ */
+- (NSExpression *)mgl_expressionByReplacingTokensWithKeyPaths;
+
+@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;
+
+- (id)mgl_has:(id)element;
+
+@end
+
+@interface MGLColor (MGLExpressionAdditions)
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+@end
+
+@interface NSExpression (MGLExpressionAdditions)
+
+- (NSExpression *)mgl_expressionWithContext:(NSDictionary<NSString *, NSExpression *> *)context;
+
+
+- (id)mgl_has:(id)element;
+
+@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..a73b1a61ba 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,14 @@
@end
+@interface NSPredicate (MGLExpressionAdditions)
+
++ (instancetype)mgl_predicateWithJSONObject:(id)object;
+
+@property (nonatomic, readonly) id mgl_jsonExpressionObject;
+
+- (id)mgl_if:(id)firstValue, ...;
+
+- (id)mgl_match:(NSExpression *)firstCase, ...;
+
+@end
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index e0511d8740..bbd324bb63 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -1,6 +1,9 @@
#import "NSPredicate+MGLAdditions.h"
#import "MGLValueEvaluator.h"
+#import "MGLStyleValue_Private.h"
+
+#include <mbgl/style/conversion/filter.hpp>
class FilterEvaluator {
public:
@@ -194,37 +197,198 @@ public:
NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) {
return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"];
}
+
+ NSPredicate *operator()(mbgl::style::ExpressionFilter filter) {
+ id jsonObject = MGLJSONObjectFromMBGLExpression(*filter.expression);
+ return [NSPredicate mgl_predicateWithJSONObject:jsonObject];
+ }
};
@implementation NSPredicate (MGLAdditions)
- (mbgl::style::Filter)mgl_filter
{
- if ([self isEqual:[NSPredicate predicateWithValue:YES]])
- {
- return mbgl::style::AllFilter();
+ mbgl::style::conversion::Error valueError;
+ NSArray *jsonObject = self.mgl_jsonExpressionObject;
+ auto value = mbgl::style::conversion::convert<mbgl::style::Filter>(mbgl::style::conversion::makeConvertible(jsonObject), valueError);
+
+ if (!value) {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Invalid filter value: %@", @(valueError.message.c_str())];
+ return {};
}
+ mbgl::style::Filter filter = std::move(*value);
+
+ return filter;
+}
+
++ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
+{
+ FilterEvaluator evaluator;
+ return mbgl::style::Filter::visit(filter, evaluator);
+}
- if ([self isEqual:[NSPredicate predicateWithValue:NO]])
- {
- return mbgl::style::AnyFilter();
+@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;
+}
- if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("])
- {
++ (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:@"%@ >= %@" 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<NSPredicate *> *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ if (subpredicates.count == 2) {
+ // Determine if the expression is of BETWEEN type
+ if ([subpredicates[0] isKindOfClass:[NSComparisonPredicate class]] &&
+ [subpredicates[1] isKindOfClass:[NSComparisonPredicate class]]) {
+ NSComparisonPredicate *leftCondition = (NSComparisonPredicate *)subpredicates[0];
+ NSComparisonPredicate *rightCondition = (NSComparisonPredicate *)subpredicates[1];
+
+ NSArray *limits;
+ NSExpression *leftConditionExpression;
+
+ if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.rightExpression, rightCondition.rightExpression];
+ leftConditionExpression = leftCondition.leftExpression;
+
+ } else if (leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.leftExpression, rightCondition.rightExpression];
+ leftConditionExpression = leftCondition.rightExpression;
+
+ } else if(leftCondition.predicateOperatorType == NSLessThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.leftExpression, rightCondition.leftExpression];
+ leftConditionExpression = leftCondition.rightExpression;
+
+ } else if(leftCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType &&
+ rightCondition.predicateOperatorType == NSGreaterThanOrEqualToPredicateOperatorType) {
+ limits = @[leftCondition.rightExpression, rightCondition.leftExpression];
+ leftConditionExpression = leftCondition.leftExpression;
+ }
+
+ if (limits && leftConditionExpression) {
+ return [NSPredicate predicateWithFormat:@"%@ BETWEEN %@", leftConditionExpression, [NSExpression expressionForAggregate:limits]];
+ }
+ }
+ }
+ return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];
+ }
+ if ([op isEqualToString:@"any"]) {
+ NSArray *subpredicates = MGLSubpredicatesWithJSONObjects([objects subarrayWithRange:NSMakeRange(1, objects.count - 1)]);
+ return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates];
+ }
+
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:object];
+ return [NSComparisonPredicate predicateWithLeftExpression:expression
+ rightExpression:[NSExpression expressionForConstantValue:@YES]
+ modifier:NSDirectPredicateModifier
+ type:NSEqualToPredicateOperatorType
+ options:0];
+
+}
+
+- (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 {};
+ return nil;
}
-+ (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter
-{
- FilterEvaluator evaluator;
- return mbgl::style::Filter::visit(filter, evaluator);
+- (id)mgl_if:(id)firstValue, ... {
+
+ if ([self evaluateWithObject:nil]) {
+ return firstValue;
+ }
+
+ id eachExpression;
+ va_list argumentList;
+ va_start(argumentList, firstValue);
+
+ while ((eachExpression = va_arg(argumentList, id))) {
+ if ([eachExpression isKindOfClass:[NSComparisonPredicate class]]) {
+ id valueExpression = va_arg(argumentList, id);
+ if ([eachExpression evaluateWithObject:nil]) {
+ return valueExpression;
+ }
+ } else {
+ return eachExpression;
+ }
+ }
+ va_end(argumentList);
+
+ return nil;
+}
+
+- (id)mgl_match:(NSExpression *)firstCase, ... {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Match expressions lack underlying Objective-C implementations."];
+ return nil;
}
@end
diff --git a/platform/darwin/src/NSString+MGLAdditions.h b/platform/darwin/src/NSString+MGLAdditions.h
index d82ecaa671..4888c7a00f 100644
--- a/platform/darwin/src/NSString+MGLAdditions.h
+++ b/platform/darwin/src/NSString+MGLAdditions.h
@@ -20,6 +20,17 @@ 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.
+
+ @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..d645490eb3 100644
--- a/platform/darwin/src/NSString+MGLAdditions.m
+++ b/platform/darwin/src/NSString+MGLAdditions.m
@@ -1,5 +1,9 @@
#import "NSString+MGLAdditions.h"
+#if TARGET_OS_OSX
+ #import <Availability.h>
+#endif
+
@implementation NSString (MGLAdditions)
- (NSRange)mgl_wholeRange {
@@ -13,14 +17,9 @@
- (NSString *)mgl_titleCasedStringWithLocale:(NSLocale *)locale {
NSMutableString *string = self.mutableCopy;
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:)]) {
+ if (@available(iOS 11.0, macOS 10.13.0, *)) {
orthography = [NSOrthography defaultOrthographyForLanguage:locale.localeIdentifier];
}
-#pragma clang diagnostic pop
-#endif
[string enumerateLinguisticTagsInRange:string.mgl_wholeRange scheme:NSLinguisticTagSchemeLexicalClass options:0 orthography:orthography usingBlock:^(NSString * _Nonnull tag, NSRange tokenRange, NSRange sentenceRange, BOOL * _Nonnull stop) {
NSString *word = [string substringWithRange:tokenRange];
if (word.length > 3
@@ -41,6 +40,21 @@
return string;
}
+- (NSString *)mgl_stringByTransliteratingIntoScript:(NSString *)script {
+ 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;
+}
+
@end
@implementation NSAttributedString (MGLAdditions)
diff --git a/platform/darwin/src/NSValue+MGLAdditions.h b/platform/darwin/src/NSValue+MGLAdditions.h
index f3026a389f..9222f04620 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.h
+++ b/platform/darwin/src/NSValue+MGLAdditions.h
@@ -29,6 +29,19 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly) CLLocationCoordinate2D MGLCoordinateValue;
/**
+ Creates a new value object containing the specified Mapbox map point structure.
+
+ @param point The value for the new object.
+ @return A new value object that contains the coordinate and zoom level information.
+ */
++ (instancetype)valueWithMGLMapPoint:(MGLMapPoint)point;
+
+/**
+ The Mapbox map point structure representation of the value.
+ */
+@property (readonly) MGLMapPoint MGLMapPointValue;
+
+/**
Creates a new value object containing the specified Mapbox coordinate span
structure.
diff --git a/platform/darwin/src/NSValue+MGLAdditions.m b/platform/darwin/src/NSValue+MGLAdditions.m
index 1383056944..b32445dab7 100644
--- a/platform/darwin/src/NSValue+MGLAdditions.m
+++ b/platform/darwin/src/NSValue+MGLAdditions.m
@@ -14,6 +14,16 @@
return coordinate;
}
++ (instancetype)valueWithMGLMapPoint:(MGLMapPoint)point {
+ return [self valueWithBytes:&point objCType:@encode(MGLMapPoint)];
+}
+
+-(MGLMapPoint) MGLMapPointValue {
+ MGLMapPoint point;
+ [self getValue:&point];
+ return point;
+}
+
+ (instancetype)valueWithMGLCoordinateSpan:(MGLCoordinateSpan)span {
return [self valueWithBytes:&span objCType:@encode(MGLCoordinateSpan)];
}
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 f291c0805a..050fa62c78 100644
--- a/platform/darwin/src/headless_backend_eagl.mm
+++ b/platform/darwin/src/headless_backend_eagl.mm
@@ -6,52 +6,48 @@
namespace mbgl {
-struct EAGLImpl : public HeadlessBackend::Impl {
- EAGLImpl() {
+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() {
- impl.reset();
- impl = std::make_unique<EAGLImpl>();
-
+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..5961b61133 100644
--- a/platform/darwin/test/MGLAttributionInfoTests.m
+++ b/platform/darwin/test/MGLAttributionInfoTests.m
@@ -17,7 +17,7 @@
@"<a class=\"mapbox-improve-map\" href=\"https://www.mapbox.com/map-feedback/\" target=\"_blank\">Improve this map</a>",
};
- NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array];
+ NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array];
for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) {
NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i]
fontSize:0
@@ -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
}
@@ -67,7 +67,7 @@
CGFloat fontSize = 72;
MGLColor *color = [MGLColor redColor];
- NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array];
+ NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array];
for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) {
NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i]
fontSize:72
@@ -109,7 +109,7 @@
@"Hello World",
};
- NS_MUTABLE_ARRAY_OF(MGLAttributionInfo *) *infos = [NSMutableArray array];
+ NSMutableArray<MGLAttributionInfo *> *infos = [NSMutableArray array];
for (NSUInteger i = 0; i < sizeof(htmlStrings) / sizeof(htmlStrings[0]); i++) {
NSArray *subinfos = [MGLAttributionInfo attributionInfosFromHTMLString:htmlStrings[i]
fontSize:0
diff --git a/platform/darwin/test/MGLBackgroundStyleLayerTests.mm b/platform/darwin/test/MGLBackgroundStyleLayerTests.mm
index c96a4fe7fa..de8080f425 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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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..d7bf2a5afd 100644
--- a/platform/darwin/test/MGLCircleStyleLayerTests.mm
+++ b/platform/darwin/test/MGLCircleStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -52,40 +52,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +98,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleBlur, pedanticFunctionExpression,
+ @"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 +124,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +170,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleColor, pedanticFunctionExpression,
+ @"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 +196,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +242,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleOpacity, pedanticFunctionExpression,
+ @"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 +268,89 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +358,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleRadius, pedanticFunctionExpression,
+ @"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 +384,89 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +474,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeColor, pedanticFunctionExpression,
+ @"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 +500,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +546,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeOpacity, pedanticFunctionExpression,
+ @"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 +572,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +618,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.circleStrokeWidth, pedanticFunctionExpression,
+ @"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 +644,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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/MGLDocumentationExampleTests.swift b/platform/darwin/test/MGLDocumentationExampleTests.swift
index 668e5f57f8..9edb33a078 100644
--- a/platform/darwin/test/MGLDocumentationExampleTests.swift
+++ b/platform/darwin/test/MGLDocumentationExampleTests.swift
@@ -73,14 +73,14 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
XCTAssertNotNil(mapView.style?.source(withIdentifier: "lines"))
}
- func testMGLRasterSource() {
+ func testMGLRasterTileSource() {
//#-example-code
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
@@ -88,14 +88,34 @@ 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() {
+ func testMGLVectorTileSource() {
//#-example-code
- let source = MGLVectorSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
+ let source = MGLVectorTileSource(identifier: "pois", tileURLTemplates: ["https://example.com/vector-tiles/{z}/{x}/{y}.mvt"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
@@ -131,18 +151,21 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLCircleStyleLayer() {
- let population = MGLVectorSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
+ let population = MGLVectorTileSource(identifier: "population", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(population)
//#-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: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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
@@ -151,18 +174,21 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLLineStyleLayer() {
- let trails = MGLVectorSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!)
+ let trails = MGLVectorTileSource(identifier: "trails", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(trails)
//#-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: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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
@@ -171,13 +197,17 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLFillStyleLayer() {
- let parks = MGLVectorSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!)
+ let parks = MGLVectorTileSource(identifier: "parks", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(parks)
//#-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
@@ -186,39 +216,57 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLFillExtrusionStyleLayer() {
- let buildings = MGLVectorSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!)
+ let buildings = MGLVectorTileSource(identifier: "buildings", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(buildings)
//#-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: "mgl_interpolate:withCurveType:parameters:stops:(magnitude, 'linear', nil, %@)",
+ [0: 0,
+ 6: 1])
+ layer.heatmapIntensity = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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")!)
+ let pois = MGLVectorTileSource(identifier: "pois", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(pois)
//#-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
@@ -227,33 +275,60 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
}
func testMGLRasterStyleLayer() {
- let source = MGLRasterSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
+ let source = MGLRasterTileSource(identifier: "clouds", tileURLTemplates: ["https://example.com/raster-tiles/{z}/{x}/{y}.png"], options: [
.minimumZoomLevel: 9,
.maximumZoomLevel: 16,
.tileSize: 512,
.attributionInfos: [
- MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "http://mapbox.com"))
+ MGLAttributionInfo(title: NSAttributedString(string: "© Mapbox"), url: URL(string: "https://mapbox.com"))
]
])
mapView.style?.addSource(source)
//#-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: "https://mapbox.com"))
+ ]
+ ])
+ mapView.style?.addSource(source)
+
+ let canals = MGLVectorTileSource(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")!)
+ let terrain = MGLVectorTileSource(identifier: "terrain", configurationURL: URL(string: "https://example.com/style.json")!)
mapView.style?.addSource(terrain)
//#-example-code
let layer = MGLLineStyleLayer(identifier: "contour", source: terrain)
layer.sourceLayerIdentifier = "contours"
- layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && ele >= 1500.0")
+ layer.predicate = NSPredicate(format: "(index == 5 || index == 10) && CAST(ele, 'NSNumber') >= 1500.0")
mapView.style?.addLayer(layer)
//#-end-example-code
@@ -295,7 +370,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
#endif
class MGLStyle {
- static func satelliteStreetsStyleURL() -> URL {
+ static var satelliteStreetsStyleURL: URL {
return MGLDocumentationExampleTests.styleURL
}
}
@@ -303,7 +378,7 @@ class MGLDocumentationExampleTests: XCTestCase, MGLMapViewDelegate {
//#-example-code
let camera = MGLMapCamera(lookingAtCenter: CLLocationCoordinate2D(latitude: 37.7184, longitude: -122.4365), fromDistance: 100, pitch: 20, heading: 0)
- let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL(), camera: camera, size: CGSize(width: 320, height: 480))
+ let options = MGLMapSnapshotOptions(styleURL: MGLStyle.satelliteStreetsStyleURL, camera: camera, size: CGSize(width: 320, height: 480))
options.zoomLevel = 10
let snapshotter = MGLMapSnapshotter(options: options)
@@ -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..4de1d81aa9 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)
@@ -49,31 +50,32 @@ class MGLDocumentationGuideTests: XCTestCase, MGLMapViewDelegate {
styleLoadingExpectation.fulfill()
}
- func testUsingStyleFunctionsAtRuntime$Stops() {
+ func testMigratingToExpressions$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: [NSNumber: 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: [NSNumber: 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: "mgl_step:from:stops:(mag, %@, %@)",
+ stops[0]!, stops)
}
- func testUsingStyleFunctionsAtRuntime$Linear() {
+ func testMigratingToExpressions$Linear() {
//#-example-code
let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
let symbolSource = MGLSource(identifier: "source")
@@ -83,132 +85,192 @@ 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: [NSNumber: 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: [NSNumber: 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: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
+ stops)
#else
- layer.circleColor = MGLStyleValue(interpolationMode: .exponential,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+ layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
+ stops)
#endif
- layer.circleRadius = MGLStyleValue(rawValue: 10)
+ layer.circleRadius = NSExpression(forConstantValue: 10)
mapView.style?.insertLayer(layer, below: symbolLayer)
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Exponential() {
+ func testMigratingToExpressions$LinearConvenience() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ #if os(macOS)
+ let stops: [NSNumber: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+ ]
+ #else
+ let stops: [NSNumber: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+ ]
+ #endif
+
+ //#-example-code
+ layer.circleColor = NSExpression(forMGLInterpolating: NSExpression(forKeyPath: "mag"), curveType: .linear, parameters: nil, stops: NSExpression(forConstantValue: stops))
+ //#-end-example-code
+
+ layer.circleRadius = NSExpression(forConstantValue: 10)
+ mapView.style?.addLayer(layer)
+
+ }
+ func testMigratingToExpressions$Exponential() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-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: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
+ stops)
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Interval() {
+ func testMigratingToExpressions$ExponentialConvenience() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ //#-example-code
+ let stops = [
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
+ ]
+
+ layer.circleRadius = NSExpression(forMGLInterpolating: NSExpression.zoomLevelVariable, curveType: MGLExpressionInterpolationMode.exponential, parameters: NSExpression(forConstantValue: 1.5), stops: NSExpression(forConstantValue: stops))
+ //#-end-example-code
+ }
+ func testMigratingToExpressions$Interval() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-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: [NSNumber: 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: "mgl_step:from:stops:(mag, %@, %@)",
+ 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: [NSNumber: 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: "mgl_step:from:stops:(mag, %@, %@)",
+ UIColor.green, stops)
#endif
//#-end-example-code
}
- func testUsingStyleFunctionsAtRuntime$Categorical() {
+ func testMigratingToExpressions$Categorical() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
//#-example-code
#if os(macOS)
- let categoricalStops = [
- "earthquake": MGLStyleValue<NSColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
- ]
-
- layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)])
+ let defaultColor = NSColor.blue
+ layer.circleColor = NSExpression(
+ format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ NSColor.orange, NSColor.red, NSColor.yellow, defaultColor)
#else
- let categoricalStops = [
- "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
- "explosion": MGLStyleValue(rawValue: .red),
- "quarry blast": MGLStyleValue(rawValue: .yellow),
- ]
-
- layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+ let defaultColor = UIColor.blue
+ layer.circleColor = NSExpression(format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ UIColor.orange, UIColor.red, UIColor.yellow, defaultColor)
+ #endif
+ //#-end-example-code
+ }
+
+ func testMigratingToExpressions$CategoricalValue() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ //#-example-code
+ #if os(macOS)
+ let stops : [String : NSColor] = ["earthquake" : NSColor.orange,
+ "explosion" : NSColor.red,
+ "quarry blast" : NSColor.yellow]
+ layer.circleColor = NSExpression(
+ format: "FUNCTION(%@, 'valueForKeyPath:', type)",
+ stops)
+ #else
+ let stops : [String : UIColor] = ["earthquake" : UIColor.orange,
+ "explosion" : UIColor.red,
+ "quarry blast" : UIColor.yellow]
+ layer.circleColor = NSExpression(
+ format: "FUNCTION(%@, 'valueForKeyPath:', type)",
+ stops)
#endif
//#-end-example-code
}
+ func testMigratingToExpressions$Identity() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+ let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+
+ //#-example-code
+ layer.circleRadius = NSExpression(forKeyPath: "mag")
+ //#-end-example-code
+ }
- func testUsingStyleFunctionsAtRuntime$Identity() {
+ func testMigratingToExpressions$Multiply() {
let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
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(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
+ //#-end-example-code
+ }
+
+ func testMigratingToExpressions$Cast() {
+ let source = MGLShapeSource(identifier: "circles", shape: nil, options: nil)
+
+ //#-example-code
+ let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
+ magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
+ mapView.style?.addLayer(magnitudeLayer)
//#-end-example-code
}
}
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index ad0833a068..6d710fdcfe 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,942 @@ 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 expressionWithMGLJSONObject:@[@"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 expressionWithMGLJSONObject:@[@"heatmap-density"]], expression);
+ NSMutableDictionary *context = [@{@"heatmapDensity": @1} mutableCopy];
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:context], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"geometryType"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"geometry-type"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$geometryType"].mgl_jsonExpressionObject, @[@"geometry-type"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"geometry-type"]], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"featureIdentifier"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"id"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$featureIdentifier"].mgl_jsonExpressionObject, @[@"id"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"id"]], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"featureAttributes"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, @[@"properties"]);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$featureAttributes"].mgl_jsonExpressionObject, @[@"properties"]);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:@[@"properties"]], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForVariable:@"loremIpsum"];
+ NSArray *jsonExpression = @[@"var", @"loremIpsum"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"$loremIpsum"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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:@"MGL_LET('loremIpsum', 'Lorem ipsum dolor sit amet', uppercase($loremIpsum))", context];
+ NSExpression *compatibilityExpression = [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(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:[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 expressionWithMGLJSONObject:@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 expressionWithMGLJSONObject:@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 expressionWithMGLJSONObject:[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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"%@.population", @{@"population": MGLConstantExpression(@12000)}];
+ NSArray *jsonExpression = @[@"get", @"population", @[@"literal", @{@"population": @12000}]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSArray *arguments = @[MGLConstantExpression(@1), MGLConstantExpression(@1), MGLConstantExpression(@1)];
+ NSExpression *expression = [NSExpression expressionForFunction:@"add:to:" arguments:arguments];
+ NSArray *jsonExpression = @[@"+", @1, @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ jsonExpression = @[@"+", @[@"+", @1, @1], @1];
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"1 + 1 + 1"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], [NSExpression expressionWithFormat:@"1 + 1 + 1"]);
+ }
+ {
+ 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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonExpression = @[@"ceil", @1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonExpression = @[@"ceil", @-1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@2)]];
+ NSArray *jsonExpression = @[@"ceil", @2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ceiling:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonExpression = @[@"ceil", @-2];
+ 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 = @[@"abs", @2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"abs:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonExpression = @[@"abs", @-2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonExpression = @[@"floor", @1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonExpression = @[@"floor", @-1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@2)]];
+ NSArray *jsonExpression = @[@"floor", @2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"floor:" arguments:@[MGLConstantExpression(@-2)]];
+ NSArray *jsonExpression = @[@"floor", @-2];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@1.5)]];
+ NSArray *jsonExpression = @[@"round", @1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-1.5)]];
+ NSArray *jsonExpression = @[@"round", @-1.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-2);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@2.5)]];
+ NSArray *jsonExpression = @[@"round", @2.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @3);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_round:" arguments:@[MGLConstantExpression(@-2.5)]];
+ NSArray *jsonExpression = @[@"round", @-2.5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @-3);
+ }
+}
+
+- (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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"ln:" arguments:arguments];
+ NSArray *jsonExpression = @[@"ln", @1, @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_log2:" arguments:@[MGLConstantExpression(@1024)]];
+ NSArray *jsonExpression = @[@"log2", @1024];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @10);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_acos:" arguments:@[MGLConstantExpression(@1)]];
+ NSArray *jsonExpression = @[@"acos", @1];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_cos:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"cos", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @1);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_asin:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"asin", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_sin:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"sin", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_atan:" arguments:@[MGLConstantExpression(@20)]];
+ NSArray *jsonExpression = @[@"atan", @20];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ NSNumber *value = [expression expressionValueWithObject:nil context:nil];
+ XCTAssertEqualWithAccuracy(value.doubleValue, 1.52, 0.001);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_tan:" arguments:@[MGLConstantExpression(@0)]];
+ NSArray *jsonExpression = @[@"tan", @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @0);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testStringFormattingExpressionObject {
+ NSArray *arguments = @[MGLConstantExpression(@"MacDonald")];
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION('Old', 'stringByAppendingString:', 'MacDonald')"];
+ NSExpression *aftermarketExpression = [NSExpression expressionWithFormat:@"mgl_join({'Old', 'MacDonald'})"];
+ NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(aftermarketExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([aftermarketExpression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], aftermarketExpression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"uppercase:" arguments:arguments];
+ NSArray *jsonExpression = @[@"upcase", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"lowercase:" arguments:arguments];
+ NSArray *jsonExpression = @[@"downcase", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"length:" arguments:arguments];
+ NSArray *jsonExpression = @[@"length", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject: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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_number')"];
+ NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'doubleValue')"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'floatValue')"].mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'decimalValue')"].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 expressionWithMGLJSONObject:jsonExpression],
+ [NSExpression expressionWithFormat:@"CAST(postalCode, 'NSNumber')"]);
+ }
+ {
+ 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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(postalCode, 'NSNumber')"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(postalCode, 'mgl_numberWithFallbackValues:')"];
+ NSArray *jsonExpression = @[@"to-number", @[@"get", @"postalCode"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"postalCode": @"02134"} context:nil], @02134);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"CAST(number, 'NSString')"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(number, 'stringValue')"];
+ NSArray *jsonExpression = @[@"to-string", @[@"get", @"number"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5");
+ XCTAssertEqualObjects([compatibilityExpression expressionValueWithObject:@{@"number": @1.5} context:nil], @"1.5");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testInterpolationExpressionObject {
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@100), @10: MGLConstantExpression(@200)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, 'linear', nil, %@)", stops];
+ NSExpression *compatibilityExpression = [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(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@1: MGLConstantExpression(@2), @3: MGLConstantExpression(@6)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, 'exponential', 2, %@)", stops];
+ NSArray *jsonExpression = @[@"interpolate", @[@"exponential", @2], @[@"get", @"x"], @1, @2, @3, @6];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@0), @100: MGLConstantExpression(@100)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(x, '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 expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:(x, 11, %@)", stops];
+ NSExpression *compatibilityExpression = [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(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, 11, %@)", stops];
+ NSArray *jsonExpression = @[@"step", @[@"zoom"], @11, @0, @111, @1, @1111];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testMatchExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(2 - 1, %@, %@, %@, %@, 'default')", MGLConstantExpression(@1),
+ MGLConstantExpression(@"one"),
+ MGLConstantExpression(@0),
+ MGLConstantExpression(@"zero")];
+ NSExpression *predicate = [NSExpression expressionWithFormat:@"2 - 1"];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_match:', %@)", predicate, @[MGLConstantExpression(@1),
+ MGLConstantExpression(@"one"),
+ MGLConstantExpression(@0),
+ MGLConstantExpression(@"zero"),
+ MGLConstantExpression(@"default")]];
+ NSArray *jsonExpression = @[@"match", @[@"-", @2, @1], @1, @"one", @0, @"zero", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_MATCH(2 * 1, %@, %@, 'default')", MGLConstantExpression(@1), MGLConstantExpression(@"one")];
+ NSArray *jsonExpression = @[@"match", @[@"*", @2, @1], @1, @"one", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+- (void)testCoalesceExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"mgl_coalesce(%@)",
+ @[[NSExpression expressionForKeyPath:@"x"],
+ [NSExpression expressionForKeyPath:@"y"],
+ [NSExpression expressionForKeyPath:@"z"],
+ [NSExpression expressionForConstantValue:@0]]];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_coalesce')", @[[NSExpression expressionForKeyPath:@"x"],
+ [NSExpression expressionForKeyPath:@"y"],
+ [NSExpression expressionForKeyPath:@"z"],
+ [NSExpression expressionForConstantValue:@0]]];
+ NSArray *jsonExpression = @[@"coalesce", @[@"get", @"x"], @[@"get", @"y"], @[@"get", @"z"], @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+
+}
+
+- (void)testConditionalExpressionObject {
+ {
+ 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 expressionWithMGLJSONObject: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);
+ expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, TRUE, %@, TRUE, FALSE)",
+ MGLConstantExpression([NSPredicate predicateWithFormat:@"0 = 1"]),
+ MGLConstantExpression([NSPredicate predicateWithFormat:@"1 = 2"])];
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(%@, 'mgl_if:', %@)", [NSPredicate predicateWithFormat:@"1 = 2"], @[MGLConstantExpression(@YES), MGLConstantExpression(@NO)]];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ expression = [NSExpression expressionWithFormat:@"TERNARY(1 = 2, YES, NO)"];
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_IF(%@, %@, %@, %@, %@)",
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 2"]],
+ MGLConstantExpression(@YES),
+ [NSExpression expressionWithFormat:@"%@", [NSPredicate predicateWithFormat:@"1 = 1"]],
+ MGLConstantExpression(@YES),
+ MGLConstantExpression(@NO)];
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @[@"==", @1, @1], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+ {
+ NSArray *jsonExpression = @[
+ @"case",
+ @[
+ @"<",
+ @[@"get", @"area"],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name_en"]
+ ];
+ NSExpression *expression = [NSExpression expressionWithFormat:@"TERNARY(area < 80000, abbr, name_en)"];
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ }
+}
+
+- (void)testLookupExpressionObject {
+ {
+ NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9),
+ MGLConstantExpression(@8),
+ MGLConstantExpression(@7)]];
+ NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:"
+ arguments:@[array, MGLConstantExpression(@1)]];
+ NSArray *jsonExpression = @[@"at", @1, @[ @"literal", @[@9, @8, @7]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *array = [NSExpression expressionForAggregate:@[MGLConstantExpression(@9),
+ MGLConstantExpression(@8),
+ MGLConstantExpression(@7)]];
+ NSExpression *expression = [NSExpression expressionForFunction:@"objectFrom:withIndex:"
+ arguments:@[array, [NSExpression expressionForKeyPath:@"x"]]];
+ NSArray *jsonExpression = @[@"at", @[@"get", @"x"], @[ @"literal", @[@9, @8, @7]]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[[NSExpression expressionForEvaluatedObject],
+ [NSExpression expressionForConstantValue:@"x"]]];
+ NSExpression *compatibilityExpression = [NSExpression expressionWithFormat:@"FUNCTION(self, 'mgl_has:', 'x')"];
+ NSArray *jsonExpression = @[@"has", @"x"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects(compatibilityExpression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[MGLConstantExpression(@{@"x": MGLConstantExpression(@0)}),
+ MGLConstantExpression(@"x")]];
+ NSArray *jsonExpression = @[@"has", @"x", @[@"literal", @{@"x": @0}]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [NSExpression expressionForFunction:@"mgl_does:have:"
+ arguments:@[[NSExpression expressionForVariable:@"featureAttributes"],
+ [NSExpression expressionForConstantValue:@"x"]]];
+ NSArray *jsonExpression = @[@"has", @"x", @[@"properties"]];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression;
+ expression = [NSExpression expressionWithFormat:@"TERNARY(key != nil, 1, 0)"];
+ NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key"], [NSNull null]], @1, @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{} context:nil], @NO);
+ XCTAssertEqualObjects([expression expressionValueWithObject:@{@"key": @"🗝"} context:nil], @YES);
+ }
+ {
+ NSDictionary *dictionary = @{@"key": @"🔑"};
+ NSExpression *expression;
+ expression = [NSExpression expressionWithFormat:@"TERNARY(%@.key != nil, 1, 0)", dictionary];
+ NSArray *jsonExpression = @[@"case", @[@"!=", @[@"get", @"key", @[@"literal", dictionary]], [NSNull null]], @1, @0];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ // The dictionary isn’t equal enough.
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression].description, expression.description);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @YES);
+ }
+}
+
+- (void)testGenericExpressionObject {
+ {
+ NSExpression *expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('random', 1, 2, 3, 4, 5)"];
+ NSArray *jsonExpression = @[@"random", @1, @2, @3, @4, @5];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ expression = [NSExpression expressionWithFormat:@"MGL_FUNCTION('random', 1, 2, 3, 4)"];
+ XCTAssertThrowsSpecificNamed([expression expressionValueWithObject:nil context:nil], NSException, NSInvalidArgumentException);
+ }
+}
+
+#pragma mark - Localization tests
+
+- (void)testTokenReplacement {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token}");
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"token"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{token} {token}");
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_join({token, ' ', token})"];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, '{short}', %@)", @{
+ @1: MGLConstantExpression(@"{short}"),
+ @2: @"…",
+ @3: @"{long}",
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"short"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"long"],
+ }];
+ XCTAssertEqualObjects(original.mgl_expressionByReplacingTokensWithKeyPaths, expected);
+ }
+}
+
+- (void)testLocalization {
+ {
+ NSExpression *original = MGLConstantExpression(@"");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"Old MacDonald");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = MGLConstantExpression(@"{name_en}");
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = original;
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:nil], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_fr"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"fr-CA"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name_zh-Hans"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"zh-Hans"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionForKeyPath:@"name_en"];
+ NSExpression *expected = [NSExpression expressionForKeyPath:@"name"];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"tlh"]], expected);
+ }
+ {
+ NSExpression *original = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_fr"],
+ }];
+ NSExpression *expected = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, short, %@)", @{
+ @1: [NSExpression expressionForKeyPath:@"abbr"],
+ @2: @"…",
+ @3: [NSExpression expressionForKeyPath:@"name_es"],
+ }];
+ XCTAssertEqualObjects([original mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"es-PR"]], expected);
+ }
+ {
+ NSArray *jsonExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name_en"]
+ ],
+ @5, @[@"get", @"name_en"]
+ ];
+ NSArray *localizedJSONExpression = @[
+ @"step",
+ @[@"zoom"],
+ @[
+ @"case",
+ @[
+ @"<",
+ @[
+ @"to-number",
+ @[@"get", @"area"]
+ ],
+ @80000
+ ],
+ @[@"get", @"abbr"],
+ @[@"get", @"name"]
+ ],
+ @5, @[@"get", @"name"]
+ ];
+ NSExpression *expression = [NSExpression expressionWithMGLJSONObject:jsonExpression];
+ NSExpression *localizedExpression = [expression mgl_expressionLocalizedIntoLocale:[NSLocale localeWithLocaleIdentifier:@"mul"]];
+ XCTAssertEqualObjects(localizedExpression.mgl_jsonExpressionObject, localizedJSONExpression);
+ }
+}
+
+- (void)testConvenienceInitializers {
+ {
+ NSExpression *expression = [NSExpression mgl_expressionForConditional:[NSPredicate predicateWithFormat:@"1 = 2"]
+ trueExpression:MGLConstantExpression(@YES)
+ falseExpresssion:MGLConstantExpression(@NO)];
+
+ NSArray *jsonExpression = @[@"case", @[@"==", @1, @2], @YES, @NO];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @NO);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@111), @1: MGLConstantExpression(@1111)};
+ NSExpression *expression = [NSExpression mgl_expressionForSteppingExpression:[NSExpression expressionForKeyPath:@"x"]
+ fromExpression:[NSExpression expressionForConstantValue:@11]
+ stops:[NSExpression expressionForConstantValue:stops]];
+ NSArray *jsonExpression = @[@"step", @[@"get", @"x"], @11, @0, @111, @1, @1111];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *stops = @{@0: MGLConstantExpression(@100), @10: MGLConstantExpression(@200)};
+ NSExpression *expression = [NSExpression mgl_expressionForInterpolatingExpression:[NSExpression expressionForKeyPath:@"x"]
+ withCurveType:MGLExpressionInterpolationModeLinear
+ parameters:nil
+ stops:[NSExpression expressionForConstantValue:stops]];
+ NSArray *jsonExpression = @[@"interpolate", @[@"linear"], @[@"get", @"x"], @0, @100, @10, @200];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSExpression *expression = [[NSExpression expressionForConstantValue:@"Old"] mgl_expressionByAppendingExpression:[NSExpression expressionForConstantValue:@"MacDonald"]];
+
+ NSArray *jsonExpression = @[@"concat", @"Old", @"MacDonald"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([expression expressionValueWithObject:nil context:nil], @"OldMacDonald");
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+ {
+ NSDictionary *values = @{ MGLConstantExpression(@1): MGLConstantExpression(@"one") };
+ NSExpression *expression = [NSExpression mgl_expressionForMatchingExpression:[NSExpression expressionWithFormat:@"2 * 1"]
+ inDictionary:values
+ defaultExpression:[NSExpression expressionForConstantValue:@"default"]];
+ NSArray *jsonExpression = @[@"match", @[@"*", @2, @1], @1, @"one", @"default"];
+ XCTAssertEqualObjects(expression.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSExpression expressionWithMGLJSONObject:jsonExpression], expression);
+ }
+}
+
+
@end
diff --git a/platform/darwin/test/MGLFeatureTests.mm b/platform/darwin/test/MGLFeatureTests.mm
index 818ad8200e..d135b018f4 100644
--- a/platform/darwin/test/MGLFeatureTests.mm
+++ b/platform/darwin/test/MGLFeatureTests.mm
@@ -38,7 +38,7 @@
};
features.push_back(mbgl::Feature { polygon });
- NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features);
+ NSArray<MGLShape <MGLFeature> *> *shapes = MGLFeaturesFromMBGLFeatures(features);
XCTAssertEqual(shapes.count, 3, @"All features should be converted into shapes");
MGLPointFeature *pointShape = (MGLPointFeature *)shapes[0];
@@ -69,7 +69,7 @@
[NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 4)]);
XCTAssertEqualObjects([NSValue valueWithMGLCoordinate:polygonCoordinates[3]],
[NSValue valueWithMGLCoordinate:CLLocationCoordinate2DMake(4, 1)]);
- NS_ARRAY_OF(MGLPolygon *) *interiorPolygons = polygonShape.interiorPolygons;
+ NSArray<MGLPolygon *> *interiorPolygons = polygonShape.interiorPolygons;
XCTAssertEqual(interiorPolygons.count, 1);
MGLPolygon *interiorPolygon = interiorPolygons.firstObject;
XCTAssertEqual(interiorPolygon.pointCount, 4);
@@ -103,7 +103,7 @@
vector.push_back(true);
features.push_back(pointFeature);
- NS_ARRAY_OF(MGLShape <MGLFeature> *) *shapes = MGLFeaturesFromMBGLFeatures(features);
+ NSArray<MGLShape <MGLFeature> *> *shapes = MGLFeaturesFromMBGLFeatures(features);
XCTAssertEqual(shapes.count, 1, @"All features should be converted into shapes");
MGLShape <MGLFeature> *shape = shapes.firstObject;
@@ -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..6081d104e1 100644
--- a/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillExtrusionStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -52,40 +52,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +98,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionBase, pedanticFunctionExpression,
+ @"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 +124,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +170,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionColor, pedanticFunctionExpression,
+ @"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 +196,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +242,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillExtrusionHeight, pedanticFunctionExpression,
+ @"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 +268,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +321,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +374,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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..a5019f1032 100644
--- a/platform/darwin/test/MGLFillStyleLayerTests.mm
+++ b/platform/darwin/test/MGLFillStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -52,79 +52,89 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +142,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillColor, pedanticFunctionExpression,
+ @"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 +168,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +214,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillOpacity, pedanticFunctionExpression,
+ @"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 +240,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +286,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.fillOutlineColor, pedanticFunctionExpression,
+ @"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 +312,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +365,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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/MGLGeometryTests.mm b/platform/darwin/test/MGLGeometryTests.mm
index 1c85470188..a0ddecf77e 100644
--- a/platform/darwin/test/MGLGeometryTests.mm
+++ b/platform/darwin/test/MGLGeometryTests.mm
@@ -163,4 +163,13 @@
[NSValue valueWithMGLCoordinate:quad.bottomRight],
@"Quad bottom right should be computed correctly.");
}
+
+- (void)testMGLMapPoint {
+ MGLMapPoint point = MGLMapPointForCoordinate(CLLocationCoordinate2DMake(37.936, -80.425), 0.0);
+
+ MGLMapPoint roundTrippedPoint = [NSValue valueWithMGLMapPoint:point].MGLMapPointValue;
+ XCTAssertEqual(point.x, roundTrippedPoint.x);
+ XCTAssertEqual(point.y, roundTrippedPoint.y);
+ XCTAssertEqual(point.zoomLevel, roundTrippedPoint.zoomLevel);
+}
@end
diff --git a/platform/darwin/test/MGLHeatmapColorTests.mm b/platform/darwin/test/MGLHeatmapColorTests.mm
new file mode 100644
index 0000000000..bed777ae05
--- /dev/null
+++ b/platform/darwin/test/MGLHeatmapColorTests.mm
@@ -0,0 +1,62 @@
+#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:@"mgl_step:from:stops:($heatmapDensity, %@, %@)", 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.");
+ // The contained colors aren’t object equal, even though their descriptions are.
+ XCTAssertEqualObjects(layer.heatmapColor.description, defaultExpression.description,
+ @"heatmapColor should return the default value after being unset.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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..e4b1917257
--- /dev/null
+++ b/platform/darwin/test/MGLHeatmapStyleLayerTests.mm
@@ -0,0 +1,300 @@
+// 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 predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
+ 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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression,
+ @"heatmapRadius should round-trip data expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.heatmapRadius, pedanticFunctionExpression,
+ @"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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression,
+ @"heatmapWeight should round-trip data expressions.");
+
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.heatmapWeight, pedanticFunctionExpression,
+ @"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..34937d1674
--- /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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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..5490278e98 100644
--- a/platform/darwin/test/MGLLineStyleLayerTests.mm
+++ b/platform/darwin/test/MGLLineStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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,133 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +268,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineBlur, pedanticFunctionExpression,
+ @"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 +294,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +340,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineColor, pedanticFunctionExpression,
+ @"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 +366,89 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +456,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineGapWidth, pedanticFunctionExpression,
+ @"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 +482,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +528,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineOffset, pedanticFunctionExpression,
+ @"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 +554,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +600,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineOpacity, pedanticFunctionExpression,
+ @"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 +626,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +679,139 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +819,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.lineWidth, pedanticFunctionExpression,
+ @"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..a3ee7e3433 100644
--- a/platform/darwin/test/MGLNSStringAdditionsTests.m
+++ b/platform/darwin/test/MGLNSStringAdditionsTests.m
@@ -39,4 +39,30 @@
XCTAssertEqualObjects([@"Improve This iPhone" mgl_titleCasedStringWithLocale:locale], @"Improve This iPhone");
}
+- (void)testTransliteratedString {
+ 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"], @"ロンドン");
+}
+
@end
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
index 6e6951dcdd..ab4a7e2d88 100644
--- a/platform/darwin/test/MGLPredicateTests.mm
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -23,295 +23,9 @@ namespace mbgl {
@implementation MGLPredicateTests
-- (void)testFilterization {
- {
- auto actual = [NSPredicate predicateWithValue:YES].mgl_filter;
- mbgl::style::AllFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithValue:NO].mgl_filter;
- mbgl::style::AnyFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a = 'b'"].mgl_filter;
- mbgl::style::EqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"].mgl_filter;
- mbgl::style::TypeEqualsFilter expected = { .value = mbgl::FeatureType::Point };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"].mgl_filter;
- mbgl::style::IdentifierEqualsFilter expected = { .value = UINT64_C(67086180) };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"].mgl_filter;
- mbgl::style::NotHasIdentifierFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a = nil"].mgl_filter;
- mbgl::style::NotHasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"].mgl_filter;
- mbgl::style::TypeNotEqualsFilter expected = { .value = mbgl::FeatureType::Point };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"].mgl_filter;
- mbgl::style::IdentifierNotEqualsFilter expected = { .value = UINT64_C(67086180) };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"].mgl_filter;
- mbgl::style::HasIdentifierFilter expected;
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a != 'b'"].mgl_filter;
- mbgl::style::NotEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a != nil"].mgl_filter;
- mbgl::style::HasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a < 'b'"].mgl_filter;
- mbgl::style::LessThanFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a <= 'b'"].mgl_filter;
- mbgl::style::LessThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a > 'b'"].mgl_filter;
- mbgl::style::GreaterThanFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a >= 'b'"].mgl_filter;
- mbgl::style::GreaterThanEqualsFilter expected = { .key = "a", .value = std::string("b") };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@"b", @"z"]].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::GreaterThanEqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::LessThanEqualsFilter { .key = "a", .value = std::string("z") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a IN %@", @[@"b", @"c"]].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", @[@"LineString", @"Polygon"]].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", @[@67086180, @3709678893, @3352016856, @4189833989]].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"'Mapbox' IN a"].mgl_filter, NSException, NSInvalidArgumentException);
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"{'b', 'c'} CONTAINS a"].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
- mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@"LineString", @"Polygon"], @"$type"].mgl_filter;
- mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"{67086180, 3709678893, 3352016856, 4189833989} CONTAINS %K", @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@67086180, @3709678893, @3352016856, @4189833989], @"$id"].mgl_filter;
- mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a CONTAINS 'Mapbox'"].mgl_filter, NSException, NSInvalidArgumentException);
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"].mgl_filter;
- mbgl::style::AllFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"].mgl_filter;
- mbgl::style::AnyFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"].mgl_filter;
- mbgl::style::NoneFilter expected = {
- .filters = {
- mbgl::style::AllFilter {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"].mgl_filter;
- mbgl::style::NoneFilter expected = {
- .filters = {
- mbgl::style::EqualsFilter { .key = "a", .value = std::string("b") },
- mbgl::style::EqualsFilter { .key = "c", .value = std::string("d") },
- },
- };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a == nil"].mgl_filter;
- mbgl::style::HasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a != nil"].mgl_filter;
- mbgl::style::NotHasFilter expected = { .key = "a" };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT a IN %@", @[@"b", @"c"]].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT {'b', 'c'} CONTAINS a"].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- {
- auto actual = [NSPredicate predicateWithFormat:@"NOT %@ CONTAINS a", @[@"b", @"c"]].mgl_filter;
- mbgl::style::NotInFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
- MGLAssertEqualFilters(actual, expected);
- }
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
- NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
- XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
-
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
- XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
- return NO;
- }].mgl_filter, NSException, NSInvalidArgumentException);
-}
-
- (void)testPredication {
XCTAssertNil([NSPredicate mgl_predicateWithFilter:mbgl::style::NullFilter()]);
-
+
{
mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
@@ -351,12 +65,12 @@ namespace mbgl {
mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown };
XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException);
}
-
+
{
mbgl::style::NotHasFilter filter = { .key = "a" };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = nil"]);
}
-
+
{
mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
@@ -379,32 +93,32 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::HasFilter filter = { .key = "a" };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != nil"]);
}
-
+
{
mbgl::style::LessThanFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a < 'b'"]);
}
-
+
{
mbgl::style::LessThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a <= 'b'"]);
}
-
+
{
mbgl::style::GreaterThanFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a > 'b'"]);
}
-
+
{
mbgl::style::GreaterThanEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a >= 'b'"]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -414,7 +128,7 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -424,7 +138,7 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a BETWEEN {'b', 'z'}"]);
}
-
+
{
mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
@@ -441,7 +155,7 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
@@ -458,12 +172,12 @@ namespace mbgl {
NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
}
-
+
{
mbgl::style::AllFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
}
-
+
{
mbgl::style::AllFilter filter = {
.filters = {
@@ -473,12 +187,12 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"]);
}
-
+
{
mbgl::style::AnyFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:NO]);
}
-
+
{
mbgl::style::AnyFilter filter = {
.filters = {
@@ -488,12 +202,12 @@ namespace mbgl {
};
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"]);
}
-
+
{
mbgl::style::NoneFilter filter;
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithValue:YES]);
}
-
+
{
mbgl::style::NoneFilter filter = {
.filters = {
@@ -505,69 +219,339 @@ namespace mbgl {
}
}
-- (void)testSymmetry {
- [self testSymmetryWithFormat:@"a = 1" reverseFormat:@"1 = a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a != 1" reverseFormat:@"1 != a" mustRoundTrip:YES];
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = b"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 = 1"].mgl_filter, NSException, NSInvalidArgumentException);
-
- // In the predicate format language, $ is a special character denoting a
- // variable. Use %K to escape the special feature attribute $id.
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"$id == 670861802"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a = $id"].mgl_filter, NSException, NSInvalidArgumentException);
-
- [self testSymmetryWithFormat:@"a = nil" reverseFormat:@"nil = a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a != nil" reverseFormat:@"nil != a" mustRoundTrip:YES];
-
- [self testSymmetryWithFormat:@"a < 1" reverseFormat:@"1 > a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a <= 1" reverseFormat:@"1 >= a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a > 1" reverseFormat:@"1 < a" mustRoundTrip:YES];
- [self testSymmetryWithFormat:@"a >= 1" reverseFormat:@"1 <= a" mustRoundTrip:YES];
-
- [self testSymmetryWithFormat:@"a BETWEEN {1, 2}" reverseFormat:@"1 <= a && 2 >= a" mustRoundTrip:YES];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a BETWEEN %@", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"1 <= a && 2 >= a"]
- mustRoundTrip:YES];
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"{1, 2} BETWEEN a"].mgl_filter, NSException, NSInvalidArgumentException);
- NSPredicate *betweenSetPredicate = [NSPredicate predicateWithFormat:@"a BETWEEN %@", [NSSet setWithObjects:@1, @2, nil]];
- XCTAssertThrowsSpecificNamed(betweenSetPredicate.mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1}"].mgl_filter, NSException, NSInvalidArgumentException);
- XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BETWEEN {1, 2, 3}"].mgl_filter, NSException, NSInvalidArgumentException);
-
- [self testSymmetryWithFormat:@"a IN {1, 2}" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"a IN %@", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- mustRoundTrip:YES];
+- (void)testUnsupportedFilterPredicates {
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 2"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"1 == 1"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:YES].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithValue:NO].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a BEGINSWITH 'L'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a ENDSWITH 'itude'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a LIKE 'glob?trotter'"].mgl_filter, NSException, NSInvalidArgumentException);
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a MATCHES 'i\\w{18}n'"].mgl_filter, NSException, NSInvalidArgumentException);
+ NSPredicate *selectorPredicate = [NSPredicate predicateWithFormat:@"(SELF isKindOfClass: %@)", [MGLPolyline class]];
+ XCTAssertThrowsSpecificNamed(selectorPredicate.mgl_filter, NSException, NSInvalidArgumentException);
+
+ XCTAssertThrowsSpecificNamed([NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *, id> * _Nullable bindings) {
+ XCTAssertTrue(NO, @"Predicate block should not be evaluated.");
+ return NO;
+ }].mgl_filter, NSException, NSInvalidArgumentException);
+}
- // The reverse formats here are a bit backwards because we canonicalize
- // a reverse CONTAINS to a forward IN.
- [self testSymmetryWithFormat:@"{1, 2} CONTAINS a" reverseFormat:@"{1, 2} CONTAINS a" mustRoundTrip:NO];
- [self testSymmetryWithPredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- reversePredicate:[NSPredicate predicateWithFormat:@"%@ CONTAINS a", @[@1, @2]]
- mustRoundTrip:NO];
+- (void)testComparisonPredicates {
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"x == YES"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"x"], @YES];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') < 5"];
+ NSArray *jsonExpression = @[@"<", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') > 5"];
+ NSArray *jsonExpression = @[@">", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') <= 5"];
+ NSArray *jsonExpression = @[@"<=", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') >= 5"];
+ NSArray *jsonExpression = @[@">=", @[@"to-number", @[@"get", @"x"]], @5];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSString') > 'value'"];
+ NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"x"]], @"value"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = 'b'"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType = 'Point'"];
+ NSArray *jsonExpression = @[@"==", @[@"geometry-type"], @"Point"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 67086180"];
+ NSArray *jsonExpression = @[@"==", @[@"id"], @67086180];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = nil"];
+ NSArray *jsonExpression = @[@"==", @[@"id"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a = nil"];
+ NSArray *jsonExpression = @[@"==", @[@"get", @"a"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$geometryType != 'Point'"];
+ NSArray *jsonExpression = @[@"!=", @[@"geometry-type"], @"Point"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != 67086180"];
+ NSArray *jsonExpression = @[@"!=", @[@"id"], @67086180];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier != nil"];
+ NSArray *jsonExpression = @[@"!=", @[@"id"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != 'b'"];
+ NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a != nil"];
+ NSArray *jsonExpression = @[@"!=", @[@"get", @"a"], [NSNull null]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') < 'b'"];
+ NSArray *jsonExpression = @[@"<", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') <= 'b'"];
+ NSArray *jsonExpression = @[@"<=", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') > 'b'"];
+ NSArray *jsonExpression = @[@">", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') >= 'b'"];
+ NSArray *jsonExpression = @[@">=", @[@"to-string", @[@"get", @"a"]], @"b"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(a, 'NSString') BETWEEN {'b', 'z'}"];
+ NSArray *jsonExpression =@[@"all", @[@"<=", @"b", @[@"to-string", @[@"get", @"a"]]], @[@"<=", @[@"to-string", @[@"get", @"a"]], @"z"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ NSArray *jsonExpression = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@"<=", @[@"to-number", @[@"get", @"x"]], @100]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@"<=", @10, @[@"to-number", @[@"get", @"x"]]], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"all", @[@">=", @[@"to-number", @[@"get", @"x"]], @10], @[@">=", @100, @[@"to-number", @[@"get", @"x"]]]];
+ NSExpression *limits = [NSExpression expressionForAggregate:@[[NSExpression expressionForConstantValue:@10], [NSExpression expressionForConstantValue:@100]]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"CAST(x, 'NSNumber') BETWEEN %@", limits];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:expected], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:expected]
+ mustRoundTrip:NO];
+ }
+ {
+ NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier IN { 6, 5, 4, 3}"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"!", @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO]];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT x IN { 6, 5, 4, 3}"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"NOT MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"get", @"a"], @"b", @YES, @"c", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a IN { 'b', 'c' }"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(a, 'NSString'), 'b', YES, 'c', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ IN %@", [NSExpression expressionForVariable:@"geometryType"], @[@"LineString", @"Polygon"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"get", @"x"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS x"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST(x, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"geometry-type"], @"LineString", @YES, @"Polygon", @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%@ CONTAINS %@", @[@"LineString", @"Polygon"], [NSExpression expressionForVariable:@"geometryType"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH($geometryType, 'LineString', YES, 'Polygon', YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
+ {
+ NSArray *expected = @[@"match", @[@"id"], @6, @YES, @5, @YES, @4, @YES, @3, @YES, @NO];
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"{ 6, 5, 4, 3} CONTAINS $featureIdentifier"];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, expected);
+ NSPredicate *predicateAfter = [NSPredicate predicateWithFormat:@"MGL_MATCH(CAST($featureIdentifier, 'NSNumber'), 3, YES, 4, YES, 5, YES, 6, YES, NO) == YES"];
+ auto forwardFilter = [NSPredicate mgl_predicateWithJSONObject:expected].mgl_filter;
+ NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
+ XCTAssertEqualObjects(predicateAfter, forwardPredicateAfter);
+ }
}
-- (void)testSymmetryWithFormat:(NSString *)forwardFormat reverseFormat:(NSString *)reverseFormat mustRoundTrip:(BOOL)mustRoundTrip {
- NSPredicate *forwardPredicate = [NSPredicate predicateWithFormat:forwardFormat];
- NSPredicate *reversePredicate = reverseFormat ? [NSPredicate predicateWithFormat:reverseFormat] : nil;
- [self testSymmetryWithPredicate:forwardPredicate reversePredicate:reversePredicate mustRoundTrip:mustRoundTrip];
+- (void)testCompoundPredicates {
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' AND c == 'd'"];
+ NSArray *jsonExpression = @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"a == 'b' OR c == 'd'"];
+ NSArray *jsonExpression = @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' AND c == 'd')"];
+ NSArray *jsonExpression = @[@"!", @[@"all", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT(a == 'b' OR c == 'd')"];
+ NSArray *jsonExpression = @[@"!", @[@"any", @[@"==", @[@"get", @"a"], @"b"], @[@"==", @[@"get", @"c"], @"d"]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithJSONObject:jsonExpression], predicate);
+ [self testSymmetryWithPredicate:[NSPredicate mgl_predicateWithJSONObject:jsonExpression]
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a == nil"];
+ NSArray *jsonExpression = @[@"!", @[@"==", @[@"get", @"a"], [NSNull null]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
+ {
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"NOT a != nil"];
+ NSArray *jsonExpression = @[@"!", @[@"!=", @[@"get", @"a"], [NSNull null]]];
+ XCTAssertEqualObjects(predicate.mgl_jsonExpressionObject, jsonExpression);
+ [self testSymmetryWithPredicate:predicate
+ mustRoundTrip:NO];
+ }
}
-- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate reversePredicate:(NSPredicate *)reversePredicate mustRoundTrip:(BOOL)mustRoundTrip {
+- (void)testSymmetryWithPredicate:(NSPredicate *)forwardPredicate mustRoundTrip:(BOOL)mustRoundTrip {
auto forwardFilter = forwardPredicate.mgl_filter;
NSPredicate *forwardPredicateAfter = [NSPredicate mgl_predicateWithFilter:forwardFilter];
if (mustRoundTrip) {
// A collection of ints may turn into an aggregate of longs, for
// example, so compare formats instead of the predicates themselves.
XCTAssertEqualObjects(forwardPredicate.predicateFormat, forwardPredicateAfter.predicateFormat);
- }
-
- if (reversePredicate) {
- auto reverseFilter = reversePredicate.mgl_filter;
- NSPredicate *reversePredicateAfter = [NSPredicate mgl_predicateWithFilter:reverseFilter];
- XCTAssertNotEqualObjects(reversePredicate, reversePredicateAfter);
-
- XCTAssertEqualObjects(forwardPredicateAfter, reversePredicateAfter);
+ } else {
+ XCTAssertEqualObjects(forwardPredicate, forwardPredicateAfter);
}
}
diff --git a/platform/darwin/test/MGLRasterStyleLayerTests.mm b/platform/darwin/test/MGLRasterStyleLayerTests.mm
index 7b0757eeb6..c8e454743e 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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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..727d8bf0c6 100644
--- a/platform/darwin/test/MGLSDKTestHelpers.swift
+++ b/platform/darwin/test/MGLSDKTestHelpers.swift
@@ -1,3 +1,4 @@
+import XCTest
import Foundation
class MGLSDKTestHelpers {
@@ -22,9 +23,9 @@ extension MGLSDKTestHelpers {
class func protocolMethodDescriptions(_ p: Protocol) -> Set<String> {
var methods = Set<String>()
var methodCount = UInt32()
- let methodDescriptionList: UnsafeMutablePointer<objc_method_description>! = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
+ let methodDescriptionList = protocol_copyMethodDescriptionList(p, false, true, &methodCount)
for i in 0..<Int(methodCount) {
- let description: objc_method_description = methodDescriptionList[i]
+ let description = methodDescriptionList![i]
XCTAssertNotNil(description.name?.description)
methods.insert(description.name!.description)
}
@@ -35,11 +36,16 @@ extension MGLSDKTestHelpers {
class func classMethodDescriptions(_ cls: Swift.AnyClass) -> Set<String> {
var methods = Set<String>()
var methodCount = UInt32()
- let methodList: UnsafeMutablePointer<Method?>! = class_copyMethodList(cls, &methodCount)
+ let methodList = class_copyMethodList(cls, &methodCount)
for i in 0..<Int(methodCount) {
- let method = methodList[i]
- let selector : Selector = method_getName(method)
- methods.insert(selector.description)
+ let method = methodList![i]
+ let selector = method_getName(method)
+ #if os(macOS)
+ methods.insert(selector.description)
+ #else
+ XCTAssertNotNil(selector)
+ methods.insert(selector!.description)
+ #endif
}
free(methodList)
return methods
diff --git a/platform/darwin/test/MGLShapeSourceTests.mm b/platform/darwin/test/MGLShapeSourceTests.mm
index 561af7f3d0..d3f9a599e2 100644
--- a/platform/darwin/test/MGLShapeSourceTests.mm
+++ b/platform/darwin/test/MGLShapeSourceTests.mm
@@ -297,7 +297,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/MGLSourceQueryTests.m b/platform/darwin/test/MGLSourceQueryTests.m
index d1ef180a52..b321da1ea4 100644
--- a/platform/darwin/test/MGLSourceQueryTests.m
+++ b/platform/darwin/test/MGLSourceQueryTests.m
@@ -7,8 +7,8 @@
@implementation MGLSourceQueryTests
-- (void) testQueryVectorSource {
- MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"vector" tileURLTemplates:@[@"fake"] options:nil];
+- (void) testQueryVectorTileSource {
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"vector" tileURLTemplates:@[@"fake"] options:nil];
NSSet *sourceLayers = [NSSet setWithObjects:@"buildings", @"water", nil];
NSArray* features = [source featuresInSourceLayersWithIdentifiers:sourceLayers predicate:nil];
// Source not added yet, so features is 0
diff --git a/platform/darwin/test/MGLStyleLayerTests.h b/platform/darwin/test/MGLStyleLayerTests.h
index f0b889f022..c7577819b8 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;
@@ -11,7 +14,7 @@
@interface NSString (MGLStyleLayerTestAdditions)
-@property (nonatomic, readonly, copy) NS_ARRAY_OF(NSString *) *lexicalClasses;
+@property (nonatomic, readonly, copy) NSArray<NSString *> *lexicalClasses;
@property (nonatomic, readonly, copy) NSString *lemma;
@end
diff --git a/platform/darwin/test/MGLStyleLayerTests.m b/platform/darwin/test/MGLStyleLayerTests.m
index b51fa02af4..52b36dba00 100644
--- a/platform/darwin/test/MGLStyleLayerTests.m
+++ b/platform/darwin/test/MGLStyleLayerTests.m
@@ -33,7 +33,7 @@
}
- (void)testPropertyName:(NSString *)name isBoolean:(BOOL)isBoolean {
- NS_MUTABLE_ARRAY_OF(NSString *) *components = [name componentsSeparatedByString:@"-"].mutableCopy;
+ NSMutableArray<NSString *> *components = [name componentsSeparatedByString:@"-"].mutableCopy;
if (isBoolean) {
if ([components.firstObject isEqualToString:@"is"]) {
[components removeObjectAtIndex:0];
@@ -67,7 +67,7 @@
@implementation NSString (MGLStyleLayerTestAdditions)
-- (NS_ARRAY_OF(NSString *) *)lexicalClasses {
+- (NSArray<NSString *> *)lexicalClasses {
NSOrthography *orthography = [NSOrthography orthographyWithDominantScript:@"Latn"
languageMap:@{@"Latn": @[@"en"]}];
NSLinguisticTaggerOptions options = (NSLinguisticTaggerOmitPunctuation
diff --git a/platform/darwin/test/MGLStyleLayerTests.mm.ejs b/platform/darwin/test/MGLStyleLayerTests.mm.ejs
index 5fdfc3d44e..f70f0bba23 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];
@@ -36,8 +36,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -59,50 +59,56 @@
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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) %>.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression,
+ @"<%- 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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +116,26 @@
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) %>.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.<%- objCName(property) %>, pedanticFunctionExpression,
+ @"<%- 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +154,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/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm
index 8f610e338c..6048f39ea3 100644
--- a/platform/darwin/test/MGLStyleTests.mm
+++ b/platform/darwin/test/MGLStyleTests.mm
@@ -1,6 +1,7 @@
#import <Mapbox/Mapbox.h>
#import "NSBundle+MGLAdditions.h"
+#import "MGLVectorTileSource_Private.h"
#import <mbgl/util/default_styles.hpp>
@@ -64,12 +65,6 @@
XCTAssertEqualObjects([MGLStyle darkStyleURL].absoluteString, @(mbgl::util::default_styles::dark.url));
XCTAssertEqualObjects([MGLStyle satelliteStyleURL].absoluteString, @(mbgl::util::default_styles::satellite.url));
XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURL].absoluteString, @(mbgl::util::default_styles::satelliteStreets.url));
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqualObjects([MGLStyle emeraldStyleURL].absoluteString, @"mapbox://styles/mapbox/emerald-v8");
- XCTAssertEqualObjects([MGLStyle hybridStyleURL].absoluteString, @"mapbox://styles/mapbox/satellite-hybrid-v8");
-#pragma clang diagnostic pop
}
- (void)testVersionedStyleURLs {
@@ -99,19 +94,8 @@
@(mbgl::util::default_styles::satelliteStreets.url));
XCTAssertEqualObjects([MGLStyle satelliteStreetsStyleURLWithVersion:99].absoluteString,
@"mapbox://styles/mapbox/satellite-streets-v99");
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:mbgl::util::default_styles::trafficDay.currentVersion].absoluteString,
- @(mbgl::util::default_styles::trafficDay.url));
- XCTAssertEqualObjects([MGLStyle trafficDayStyleURLWithVersion:99].absoluteString,
- @"mapbox://styles/mapbox/traffic-day-v99");
- XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:mbgl::util::default_styles::trafficNight.currentVersion].absoluteString,
- @(mbgl::util::default_styles::trafficNight.url));
- XCTAssertEqualObjects([MGLStyle trafficNightStyleURLWithVersion:99].absoluteString,
- @"mapbox://styles/mapbox/traffic-night-v99");
-#pragma clang diagnostic pop
-
- static_assert(8 == mbgl::util::default_styles::numOrderedStyles,
+
+ static_assert(6 == mbgl::util::default_styles::numOrderedStyles,
"MGLStyleTests isn’t testing all the styles in mbgl::util::default_styles.");
}
@@ -143,7 +127,7 @@
NSString *styleHeader = self.stringWithContentsOfStyleHeader;
NSError *versionedMethodError;
- NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE");
+ NSString *versionedMethodExpressionString = @(R"RE(^\+\s*\(NSURL\s*\*\s*\)\s*(?!traffic)\w+StyleURLWithVersion\s*:\s*\(\s*NSInteger\s*\)\s*version\s*\b)RE");
NSRegularExpression *versionedMethodExpression = [NSRegularExpression regularExpressionWithPattern:versionedMethodExpressionString options:NSRegularExpressionAnchorsMatchLines error:&versionedMethodError];
XCTAssertNil(versionedMethodError, @"Error compiling regular expression to search for versioned methods.");
NSUInteger numVersionedMethodDeclarations = [versionedMethodExpression numberOfMatchesInString:styleHeader options:0 range:NSMakeRange(0, styleHeader.length)];
@@ -174,89 +158,89 @@
[self.style addSource:shapeSource];
XCTAssertThrowsSpecificNamed([self.style addSource:shapeSource], NSException, @"MGLRedundantSourceException");
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"rasterSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42];
- [self.style addSource:rasterSource];
- XCTAssertThrowsSpecificNamed([self.style addSource:rasterSource], NSException, @"MGLRedundantSourceException");
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"rasterTileSource" configurationURL:[NSURL URLWithString:@".json"] tileSize:42];
+ [self.style addSource:rasterTileSource];
+ XCTAssertThrowsSpecificNamed([self.style addSource:rasterTileSource], NSException, @"MGLRedundantSourceException");
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"vectorSource" configurationURL:[NSURL URLWithString:@".json"]];
- [self.style addSource:vectorSource];
- XCTAssertThrowsSpecificNamed([self.style addSource:vectorSource], NSException, @"MGLRedundantSourceException");
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"vectorTileSource" configurationURL:[NSURL URLWithString:@".json"]];
+ [self.style addSource:vectorTileSource];
+ XCTAssertThrowsSpecificNamed([self.style addSource:vectorTileSource], NSException, @"MGLRedundantSourceException");
}
- (void)testAddingSourcesWithDuplicateIdentifiers {
- MGLVectorSource *source1 = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
- MGLVectorSource *source2 = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source1 = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source2 = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
[self.style addSource: source1];
XCTAssertThrowsSpecificNamed([self.style addSource: source2], NSException, @"MGLRedundantSourceIdentifierException");
}
- (void)testRemovingSourcesBeforeAddingThem {
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"raster-source" tileURLTemplates:@[] options:nil];
- [self.style removeSource:rasterSource];
- [self.style addSource:rasterSource];
- XCTAssertNotNil([self.style sourceWithIdentifier:rasterSource.identifier]);
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"raster-tile-source" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:rasterTileSource];
+ [self.style addSource:rasterTileSource];
+ XCTAssertNotNil([self.style sourceWithIdentifier:rasterTileSource.identifier]);
MGLShapeSource *shapeSource = [[MGLShapeSource alloc] initWithIdentifier:@"shape-source" shape:nil options:nil];
[self.style removeSource:shapeSource];
[self.style addSource:shapeSource];
XCTAssertNotNil([self.style sourceWithIdentifier:shapeSource.identifier]);
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"vector-source" tileURLTemplates:@[] options:nil];
- [self.style removeSource:vectorSource];
- [self.style addSource:vectorSource];
- XCTAssertNotNil([self.style sourceWithIdentifier:vectorSource.identifier]);
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"vector-tile-source" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:vectorTileSource];
+ [self.style addSource:vectorTileSource];
+ XCTAssertNotNil([self.style sourceWithIdentifier:vectorTileSource.identifier]);
}
- (void)testAddingSourceOfTypeABeforeSourceOfTypeBWithSameIdentifier {
- // Add a raster source
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style addSource:rasterSource];
+ // Add a raster tile source
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style addSource:rasterTileSource];
- // Attempt to remove an image source with the same identifier as the raster source
+ // Attempt to remove an image source with the same identifier as the raster tile source
MGLImageSource *imageSource = [[MGLImageSource alloc] initWithIdentifier:@"some-identifier" coordinateQuad: { } URL:[NSURL URLWithString:@"http://host/image.png"]];
[self.style removeSource:imageSource];
- // The raster source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]);
+ // The raster tile source should still be added
+ XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MGLRasterTileSource class]]);
- // Remove the raster source
- [self.style removeSource:rasterSource];
+ // Remove the raster tile source
+ [self.style removeSource:rasterTileSource];
// Add the shape source
[self.style addSource:imageSource];
- // Attempt to remove a vector source with the same identifer as the shape source
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style removeSource:vectorSource];
+ // Attempt to remove a vector tile source with the same identifer as the shape source
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style removeSource:vectorTileSource];
// The image source should still be added
XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLImageSource class]]);
// Remove the image source
[self.style removeSource:imageSource];
- // Add the vector source
- [self.style addSource:vectorSource];
+ // Add the vector tile source
+ [self.style addSource:vectorTileSource];
- // Attempt to remove the previously created raster source that has the same identifer as the shape source
- [self.style removeSource:rasterSource];
- // The vector source should still be added
- XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorSource class]]);
+ // Attempt to remove the previously created raster tile source that has the same identifer as the shape source
+ [self.style removeSource:rasterTileSource];
+ // The vector tile source should still be added
+ XCTAssertTrue([[self.style sourceWithIdentifier:imageSource.identifier] isMemberOfClass:[MGLVectorTileSource class]]);
}
- (void)testRemovingSourceInUse {
- // Add a raster source
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
- [self.style addSource:rasterSource];
+ // Add a raster tile source
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil];
+ [self.style addSource:rasterTileSource];
// Add a layer using it
- MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:rasterSource];
+ MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:rasterTileSource];
[self.style addLayer:fillLayer];
- // Attempt to remove the raster source
- [self.style removeSource:rasterSource];
+ // Attempt to remove the raster tile source
+ [self.style removeSource:rasterTileSource];
// Ensure it is still there
- XCTAssertTrue([[self.style sourceWithIdentifier:rasterSource.identifier] isMemberOfClass:[MGLRasterSource class]]);
+ XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MGLRasterTileSource class]]);
}
- (void)testLayers {
@@ -306,7 +290,7 @@
- (void)testAddingLayersWithDuplicateIdentifiers {
// Just some source
- MGLVectorSource *source = [[MGLVectorSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
+ MGLVectorTileSource *source = [[MGLVectorTileSource alloc] initWithIdentifier:@"my-source" configurationURL:[NSURL URLWithString:@"mapbox://mapbox.mapbox-terrain-v2"]];
[self.style addSource: source];
// Add initial layer
@@ -383,13 +367,6 @@
return styleHeader;
}
-- (void)testClasses {
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- XCTAssertEqual(self.style.styleClasses.count, 0);
-#pragma clang diagnostic pop
-}
-
- (void)testImages {
NSString *imageName = @"TrackingLocationMask";
#if TARGET_OS_IPHONE
@@ -443,4 +420,45 @@
XCTAssertEqualObjects(layers[startIndex++].identifier, layer4.identifier);
}
+#pragma mark Localization tests
+
+- (void)testLanguageMatching {
+ {
+ NSArray *preferences = @[@"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"en-US"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"fr"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"fr");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hans", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"zh-Hans");
+ }
+ {
+ NSArray *preferences = @[@"zh-Hant"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+ {
+ NSArray *preferences = @[@"tlh", @"en"];
+ XCTAssertEqualObjects([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences], @"en");
+ }
+ {
+ NSArray *preferences = @[@"mul"];
+ XCTAssertNil([MGLVectorTileSource preferredMapboxStreetsLanguageForPreferences:preferences]);
+ }
+}
+
@end
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..cf2f80125a 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -30,8 +30,8 @@
XCTAssertNil(layer.sourceLayerIdentifier);
XCTAssertNil(layer.predicate);
- layer.predicate = [NSPredicate predicateWithValue:NO];
- XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithValue:NO]);
+ layer.predicate = [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"];
+ XCTAssertEqualObjects(layer.predicate, [NSPredicate predicateWithFormat:@"$featureIdentifier = 1"]);
layer.predicate = nil;
XCTAssertNil(layer.predicate);
}
@@ -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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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,51 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +268,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconOffset, pedanticFunctionExpression,
+ @"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 +285,177 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +463,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconRotation, pedanticFunctionExpression,
+ @"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 +480,89 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +570,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconScale, pedanticFunctionExpression,
+ @"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 +587,271 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +859,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.maximumTextWidth, pedanticFunctionExpression,
+ @"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 +876,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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 +1046,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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 +1128,83 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1212,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textFontSize, pedanticFunctionExpression,
+ @"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 +1229,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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 +1311,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1357,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textLetterSpacing, pedanticFunctionExpression,
+ @"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 +1374,95 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1470,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textOffset, pedanticFunctionExpression,
+ @"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 +1487,177 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1665,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textRotation, pedanticFunctionExpression,
+ @"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 +1682,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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 +1764,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1810,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconColor, pedanticFunctionExpression,
+ @"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 +1836,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1882,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloBlur, pedanticFunctionExpression,
+ @"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 +1908,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +1954,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloColor, pedanticFunctionExpression,
+ @"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 +1980,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2026,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconHaloWidth, pedanticFunctionExpression,
+ @"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 +2052,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2098,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.iconOpacity, pedanticFunctionExpression,
+ @"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 +2124,139 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2264,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textColor, pedanticFunctionExpression,
+ @"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 +2290,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2336,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloBlur, pedanticFunctionExpression,
+ @"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 +2362,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2408,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloColor, pedanticFunctionExpression,
+ @"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 +2434,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2480,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textHaloWidth, pedanticFunctionExpression,
+ @"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 +2506,45 @@
{
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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_interpolate:withCurveType:parameters:stops:(keyName, '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.");
+ NSExpression *pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:(CAST(keyName, 'NSNumber'), 'linear', nil, %@)", @{@18: constantExpression}];
+ XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression,
+ @"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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +2552,16 @@
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.");
+ pedanticFunctionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{@10: pedanticFunctionExpression}];
+ XCTAssertEqualObjects(layer.textOpacity, pedanticFunctionExpression,
+ @"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 +2578,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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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:@"mgl_step:from:stops:($zoomLevel, %@, %@)", 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:@"mgl_step:from:stops:(bogus, %@, %@)", constantExpression, @{@18: constantExpression}];
+ functionExpression = [NSExpression expressionWithFormat:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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/MGLTileSetTests.mm b/platform/darwin/test/MGLTileSetTests.mm
index 4d5e1fcd05..2319f66447 100644
--- a/platform/darwin/test/MGLTileSetTests.mm
+++ b/platform/darwin/test/MGLTileSetTests.mm
@@ -66,8 +66,16 @@
// when the tile set has attribution infos
MGLAttributionInfo *mapboxInfo = [[MGLAttributionInfo alloc] initWithTitle:[[NSAttributedString alloc] initWithString:@"Mapbox"]
URL:[NSURL URLWithString:@"https://www.mapbox.com/"]];
+#if TARGET_OS_IPHONE
+ UIColor *redColor = [UIColor redColor];
+#else
+ // CSS uses the sRGB color space. In macOS 10.12 Sierra and below,
+ // -[NSColor redColor] is in the calibrated RGB space and has a slightly
+ // different sRGB value than on iOS and macOS 10.13 High Sierra.
+ NSColor *redColor = [NSColor colorWithSRGBRed:1 green:0 blue:0 alpha:1];
+#endif
NSAttributedString *gl = [[NSAttributedString alloc] initWithString:@"GL" attributes:@{
- NSBackgroundColorAttributeName: [MGLColor redColor],
+ NSBackgroundColorAttributeName: redColor,
}];
MGLAttributionInfo *glInfo = [[MGLAttributionInfo alloc] initWithTitle:gl URL:nil];
tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
@@ -82,7 +90,7 @@
#else
NSString *html = (@"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica\">"
@"<a href=\"https://www.mapbox.com/\">Mapbox</a> </font>"
- @"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; background-color: #ff2600\">GL</font>\n");
+ @"<font face=\"Helvetica\" size=\"3\" style=\"font: 12.0px Helvetica; background-color: #ff0000\">GL</font>\n");
#endif
XCTAssertEqualObjects(@(tileSet.attribution.c_str()), html);
@@ -102,6 +110,23 @@
// the scheme is reflected by the mbgl tileset
XCTAssertEqual(tileSet.scheme, mbgl::Tileset::Scheme::TMS);
+
+ // when the dem enciding is changed using an NSNumber
+ tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
+ MGLTileSourceOptionDEMEncoding: @(MGLDEMEncodingTerrarium),
+ });
+
+ // the encoding is reflected by the mbgl tileset
+ XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium);
+
+ // when the dem enciding is changed using an NSValue
+ MGLDEMEncoding terrarium = MGLDEMEncodingTerrarium;
+ tileSet = MGLTileSetFromTileURLTemplates(tileURLTemplates, @{
+ MGLTileSourceOptionDEMEncoding: [NSValue value:&terrarium withObjCType:@encode(MGLDEMEncoding)],
+ });
+
+ // the encoding is reflected by the mbgl tileset
+ XCTAssertEqual(tileSet.encoding, mbgl::Tileset::DEMEncoding::Terrarium);
}
- (void)testInvalidTileSet {
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..598a0b182b 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,18 +38,18 @@ 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;;
for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) {
- result += util::tileCount(bounds, z, tileSize);
+ result += util::tileCount(bounds, z);
}
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_database.cpp b/platform/default/mbgl/storage/offline_database.cpp
index 65c2097182..4611e69f43 100644
--- a/platform/default/mbgl/storage/offline_database.cpp
+++ b/platform/default/mbgl/storage/offline_database.cpp
@@ -10,11 +10,6 @@
namespace mbgl {
-OfflineDatabase::Statement::~Statement() {
- stmt.reset();
- stmt.clearBindings();
-}
-
OfflineDatabase::OfflineDatabase(std::string path_, uint64_t maximumCacheSize_)
: path(std::move(path_)),
maximumCacheSize(maximumCacheSize_) {
@@ -28,7 +23,7 @@ OfflineDatabase::~OfflineDatabase() {
statements.clear();
db.reset();
} catch (mapbox::sqlite::Exception& ex) {
- Log::Error(Event::Database, ex.code, ex.what());
+ Log::Error(Event::Database, (int)ex.code, ex.what());
}
}
@@ -57,13 +52,13 @@ void OfflineDatabase::ensureSchema() {
removeExisting();
connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create);
} catch (mapbox::sqlite::Exception& ex) {
- if (ex.code != mapbox::sqlite::Exception::Code::CANTOPEN && ex.code != mapbox::sqlite::Exception::Code::NOTADB) {
+ if (ex.code != mapbox::sqlite::ResultCode::CantOpen && ex.code != mapbox::sqlite::ResultCode::NotADB) {
Log::Error(Event::Database, "Unexpected error connecting to database: %s", ex.what());
throw;
}
try {
- if (ex.code == mapbox::sqlite::Exception::Code::NOTADB) {
+ if (ex.code == mapbox::sqlite::ResultCode::NotADB) {
removeExisting();
}
connect(mapbox::sqlite::ReadWrite | mapbox::sqlite::Create);
@@ -92,9 +87,7 @@ void OfflineDatabase::ensureSchema() {
}
int OfflineDatabase::userVersion() {
- auto stmt = db->prepare("PRAGMA user_version");
- stmt.run();
- return stmt.get<int>(0);
+ return static_cast<int>(getPragma<int64_t>("PRAGMA user_version"));
}
void OfflineDatabase::removeExisting() {
@@ -135,14 +128,12 @@ void OfflineDatabase::migrateToVersion6() {
transaction.commit();
}
-OfflineDatabase::Statement OfflineDatabase::getStatement(const char * sql) {
+mapbox::sqlite::Statement& OfflineDatabase::getStatement(const char* sql) {
auto it = statements.find(sql);
-
- if (it != statements.end()) {
- return Statement(*it->second);
+ if (it == statements.end()) {
+ it = statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(*db, sql)).first;
}
-
- return Statement(*statements.emplace(sql, std::make_unique<mapbox::sqlite::Statement>(db->prepare(sql))).first->second);
+ return *it->second;
}
optional<Response> OfflineDatabase::get(const Resource& resource) {
@@ -209,41 +200,40 @@ std::pair<bool, uint64_t> OfflineDatabase::putInternal(const Resource& resource,
}
optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resource& resource) {
- // clang-format off
- Statement accessedStmt = getStatement(
- "UPDATE resources SET accessed = ?1 WHERE url = ?2");
- // clang-format on
-
- accessedStmt->bind(1, util::now());
- accessedStmt->bind(2, resource.url);
- accessedStmt->run();
+ // Update accessed timestamp used for LRU eviction.
+ {
+ mapbox::sqlite::Query accessedQuery{ getStatement("UPDATE resources SET accessed = ?1 WHERE url = ?2") };
+ accessedQuery.bind(1, util::now());
+ accessedQuery.bind(2, resource.url);
+ accessedQuery.run();
+ }
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
// 0 1 2 3 4 5
"SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM resources "
- "WHERE url = ?");
+ "WHERE url = ?") };
// clang-format on
- stmt->bind(1, resource.url);
+ query.bind(1, resource.url);
- if (!stmt->run()) {
+ if (!query.run()) {
return {};
}
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.mustRevalidate = stmt->get<bool>(2);
- response.modified = stmt->get<optional<Timestamp>>(3);
+ response.etag = query.get<optional<std::string>>(0);
+ response.expires = query.get<optional<Timestamp>>(1);
+ response.mustRevalidate = query.get<bool>(2);
+ response.modified = query.get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(4);
+ auto data = query.get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<bool>(5)) {
+ } else if (query.get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -255,16 +245,13 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getResource(const Resou
}
optional<int64_t> OfflineDatabase::hasResource(const Resource& resource) {
- // clang-format off
- Statement stmt = getStatement("SELECT length(data) FROM resources WHERE url = ?");
- // clang-format on
-
- stmt->bind(1, resource.url);
- if (!stmt->run()) {
+ mapbox::sqlite::Query query{ getStatement("SELECT length(data) FROM resources WHERE url = ?") };
+ query.bind(1, resource.url);
+ if (!query.run()) {
return {};
}
- return stmt->get<optional<int64_t>>(0);
+ return query.get<optional<int64_t>>(0);
}
bool OfflineDatabase::putResource(const Resource& resource,
@@ -273,19 +260,19 @@ bool OfflineDatabase::putResource(const Resource& resource,
bool compressed) {
if (response.notModified) {
// clang-format off
- Statement update = getStatement(
+ mapbox::sqlite::Query notModifiedQuery{ getStatement(
"UPDATE resources "
"SET accessed = ?1, "
" expires = ?2, "
" must_revalidate = ?3 "
- "WHERE url = ?4 ");
+ "WHERE url = ?4 ") };
// clang-format on
- update->bind(1, util::now());
- update->bind(2, response.expires);
- update->bind(3, response.mustRevalidate);
- update->bind(4, resource.url);
- update->run();
+ notModifiedQuery.bind(1, util::now());
+ notModifiedQuery.bind(2, response.expires);
+ notModifiedQuery.bind(3, response.mustRevalidate);
+ notModifiedQuery.bind(4, resource.url);
+ notModifiedQuery.run();
return false;
}
@@ -296,7 +283,7 @@ bool OfflineDatabase::putResource(const Resource& resource,
mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate);
// clang-format off
- Statement update = getStatement(
+ mapbox::sqlite::Query updateQuery{ getStatement(
"UPDATE resources "
"SET kind = ?1, "
" etag = ?2, "
@@ -306,81 +293,83 @@ bool OfflineDatabase::putResource(const Resource& resource,
" accessed = ?6, "
" data = ?7, "
" compressed = ?8 "
- "WHERE url = ?9 ");
+ "WHERE url = ?9 ") };
// clang-format on
- update->bind(1, int(resource.kind));
- update->bind(2, response.etag);
- update->bind(3, response.expires);
- update->bind(4, response.mustRevalidate);
- update->bind(5, response.modified);
- update->bind(6, util::now());
- update->bind(9, resource.url);
+ updateQuery.bind(1, int(resource.kind));
+ updateQuery.bind(2, response.etag);
+ updateQuery.bind(3, response.expires);
+ updateQuery.bind(4, response.mustRevalidate);
+ updateQuery.bind(5, response.modified);
+ updateQuery.bind(6, util::now());
+ updateQuery.bind(9, resource.url);
if (response.noContent) {
- update->bind(7, nullptr);
- update->bind(8, false);
+ updateQuery.bind(7, nullptr);
+ updateQuery.bind(8, false);
} else {
- update->bindBlob(7, data.data(), data.size(), false);
- update->bind(8, compressed);
+ updateQuery.bindBlob(7, data.data(), data.size(), false);
+ updateQuery.bind(8, compressed);
}
- update->run();
- if (update->changes() != 0) {
+ updateQuery.run();
+ if (updateQuery.changes() != 0) {
transaction.commit();
return false;
}
// clang-format off
- Statement insert = getStatement(
+ mapbox::sqlite::Query insertQuery{ getStatement(
"INSERT INTO resources (url, kind, etag, expires, must_revalidate, modified, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ");
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9) ") };
// clang-format on
- insert->bind(1, resource.url);
- insert->bind(2, int(resource.kind));
- insert->bind(3, response.etag);
- insert->bind(4, response.expires);
- insert->bind(5, response.mustRevalidate);
- insert->bind(6, response.modified);
- insert->bind(7, util::now());
+ insertQuery.bind(1, resource.url);
+ insertQuery.bind(2, int(resource.kind));
+ insertQuery.bind(3, response.etag);
+ insertQuery.bind(4, response.expires);
+ insertQuery.bind(5, response.mustRevalidate);
+ insertQuery.bind(6, response.modified);
+ insertQuery.bind(7, util::now());
if (response.noContent) {
- insert->bind(8, nullptr);
- insert->bind(9, false);
+ insertQuery.bind(8, nullptr);
+ insertQuery.bind(9, false);
} else {
- insert->bindBlob(8, data.data(), data.size(), false);
- insert->bind(9, compressed);
+ insertQuery.bindBlob(8, data.data(), data.size(), false);
+ insertQuery.bind(9, compressed);
}
- insert->run();
+ insertQuery.run();
transaction.commit();
return true;
}
optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource::TileData& tile) {
- // clang-format off
- Statement accessedStmt = getStatement(
- "UPDATE tiles "
- "SET accessed = ?1 "
- "WHERE url_template = ?2 "
- " AND pixel_ratio = ?3 "
- " AND x = ?4 "
- " AND y = ?5 "
- " AND z = ?6 ");
- // clang-format on
+ {
+ // clang-format off
+ mapbox::sqlite::Query accessedQuery{ getStatement(
+ "UPDATE tiles "
+ "SET accessed = ?1 "
+ "WHERE url_template = ?2 "
+ " AND pixel_ratio = ?3 "
+ " AND x = ?4 "
+ " AND y = ?5 "
+ " AND z = ?6 ") };
+ // clang-format on
- accessedStmt->bind(1, util::now());
- accessedStmt->bind(2, tile.urlTemplate);
- accessedStmt->bind(3, tile.pixelRatio);
- accessedStmt->bind(4, tile.x);
- accessedStmt->bind(5, tile.y);
- accessedStmt->bind(6, tile.z);
- accessedStmt->run();
+ accessedQuery.bind(1, util::now());
+ accessedQuery.bind(2, tile.urlTemplate);
+ accessedQuery.bind(3, tile.pixelRatio);
+ accessedQuery.bind(4, tile.x);
+ accessedQuery.bind(5, tile.y);
+ accessedQuery.bind(6, tile.z);
+ accessedQuery.run();
+ }
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
// 0 1 2, 3, 4, 5
"SELECT etag, expires, must_revalidate, modified, data, compressed "
"FROM tiles "
@@ -388,31 +377,31 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
" AND pixel_ratio = ?2 "
" AND x = ?3 "
" AND y = ?4 "
- " AND z = ?5 ");
+ " AND z = ?5 ") };
// clang-format on
- stmt->bind(1, tile.urlTemplate);
- stmt->bind(2, tile.pixelRatio);
- stmt->bind(3, tile.x);
- stmt->bind(4, tile.y);
- stmt->bind(5, tile.z);
+ query.bind(1, tile.urlTemplate);
+ query.bind(2, tile.pixelRatio);
+ query.bind(3, tile.x);
+ query.bind(4, tile.y);
+ query.bind(5, tile.z);
- if (!stmt->run()) {
+ if (!query.run()) {
return {};
}
Response response;
uint64_t size = 0;
- response.etag = stmt->get<optional<std::string>>(0);
- response.expires = stmt->get<optional<Timestamp>>(1);
- response.mustRevalidate = stmt->get<bool>(2);
- response.modified = stmt->get<optional<Timestamp>>(3);
+ response.etag = query.get<optional<std::string>>(0);
+ response.expires = query.get<optional<Timestamp>>(1);
+ response.mustRevalidate = query.get<bool>(2);
+ response.modified = query.get<optional<Timestamp>>(3);
- optional<std::string> data = stmt->get<optional<std::string>>(4);
+ optional<std::string> data = query.get<optional<std::string>>(4);
if (!data) {
response.noContent = true;
- } else if (stmt->get<bool>(5)) {
+ } else if (query.get<bool>(5)) {
response.data = std::make_shared<std::string>(util::decompress(*data));
size = data->length();
} else {
@@ -425,27 +414,27 @@ optional<std::pair<Response, uint64_t>> OfflineDatabase::getTile(const Resource:
optional<int64_t> OfflineDatabase::hasTile(const Resource::TileData& tile) {
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query size{ getStatement(
"SELECT length(data) "
"FROM tiles "
"WHERE url_template = ?1 "
" AND pixel_ratio = ?2 "
" AND x = ?3 "
" AND y = ?4 "
- " AND z = ?5 ");
+ " AND z = ?5 ") };
// clang-format on
- stmt->bind(1, tile.urlTemplate);
- stmt->bind(2, tile.pixelRatio);
- stmt->bind(3, tile.x);
- stmt->bind(4, tile.y);
- stmt->bind(5, tile.z);
+ size.bind(1, tile.urlTemplate);
+ size.bind(2, tile.pixelRatio);
+ size.bind(3, tile.x);
+ size.bind(4, tile.y);
+ size.bind(5, tile.z);
- if (!stmt->run()) {
+ if (!size.run()) {
return {};
}
- return stmt->get<optional<int64_t>>(0);
+ return size.get<optional<int64_t>>(0);
}
bool OfflineDatabase::putTile(const Resource::TileData& tile,
@@ -454,7 +443,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
bool compressed) {
if (response.notModified) {
// clang-format off
- Statement update = getStatement(
+ mapbox::sqlite::Query notModifiedQuery{ getStatement(
"UPDATE tiles "
"SET accessed = ?1, "
" expires = ?2, "
@@ -463,18 +452,18 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND pixel_ratio = ?5 "
" AND x = ?6 "
" AND y = ?7 "
- " AND z = ?8 ");
+ " AND z = ?8 ") };
// clang-format on
- update->bind(1, util::now());
- update->bind(2, response.expires);
- update->bind(3, response.mustRevalidate);
- update->bind(4, tile.urlTemplate);
- update->bind(5, tile.pixelRatio);
- update->bind(6, tile.x);
- update->bind(7, tile.y);
- update->bind(8, tile.z);
- update->run();
+ notModifiedQuery.bind(1, util::now());
+ notModifiedQuery.bind(2, response.expires);
+ notModifiedQuery.bind(3, response.mustRevalidate);
+ notModifiedQuery.bind(4, tile.urlTemplate);
+ notModifiedQuery.bind(5, tile.pixelRatio);
+ notModifiedQuery.bind(6, tile.x);
+ notModifiedQuery.bind(7, tile.y);
+ notModifiedQuery.bind(8, tile.z);
+ notModifiedQuery.run();
return false;
}
@@ -485,7 +474,7 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
mapbox::sqlite::Transaction transaction(*db, mapbox::sqlite::Transaction::Immediate);
// clang-format off
- Statement update = getStatement(
+ mapbox::sqlite::Query updateQuery{ getStatement(
"UPDATE tiles "
"SET modified = ?1, "
" etag = ?2, "
@@ -498,78 +487,75 @@ bool OfflineDatabase::putTile(const Resource::TileData& tile,
" AND pixel_ratio = ?9 "
" AND x = ?10 "
" AND y = ?11 "
- " AND z = ?12 ");
+ " AND z = ?12 ") };
// clang-format on
- update->bind(1, response.modified);
- update->bind(2, response.etag);
- update->bind(3, response.expires);
- update->bind(4, response.mustRevalidate);
- update->bind(5, util::now());
- update->bind(8, tile.urlTemplate);
- update->bind(9, tile.pixelRatio);
- update->bind(10, tile.x);
- update->bind(11, tile.y);
- update->bind(12, tile.z);
+ updateQuery.bind(1, response.modified);
+ updateQuery.bind(2, response.etag);
+ updateQuery.bind(3, response.expires);
+ updateQuery.bind(4, response.mustRevalidate);
+ updateQuery.bind(5, util::now());
+ updateQuery.bind(8, tile.urlTemplate);
+ updateQuery.bind(9, tile.pixelRatio);
+ updateQuery.bind(10, tile.x);
+ updateQuery.bind(11, tile.y);
+ updateQuery.bind(12, tile.z);
if (response.noContent) {
- update->bind(6, nullptr);
- update->bind(7, false);
+ updateQuery.bind(6, nullptr);
+ updateQuery.bind(7, false);
} else {
- update->bindBlob(6, data.data(), data.size(), false);
- update->bind(7, compressed);
+ updateQuery.bindBlob(6, data.data(), data.size(), false);
+ updateQuery.bind(7, compressed);
}
- update->run();
- if (update->changes() != 0) {
+ updateQuery.run();
+ if (updateQuery.changes() != 0) {
transaction.commit();
return false;
}
// clang-format off
- Statement insert = getStatement(
+ mapbox::sqlite::Query insertQuery{ getStatement(
"INSERT INTO tiles (url_template, pixel_ratio, x, y, z, modified, must_revalidate, etag, expires, accessed, data, compressed) "
- "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)");
+ "VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)") };
// clang-format on
- insert->bind(1, tile.urlTemplate);
- insert->bind(2, tile.pixelRatio);
- insert->bind(3, tile.x);
- insert->bind(4, tile.y);
- insert->bind(5, tile.z);
- insert->bind(6, response.modified);
- insert->bind(7, response.mustRevalidate);
- insert->bind(8, response.etag);
- insert->bind(9, response.expires);
- insert->bind(10, util::now());
+ insertQuery.bind(1, tile.urlTemplate);
+ insertQuery.bind(2, tile.pixelRatio);
+ insertQuery.bind(3, tile.x);
+ insertQuery.bind(4, tile.y);
+ insertQuery.bind(5, tile.z);
+ insertQuery.bind(6, response.modified);
+ insertQuery.bind(7, response.mustRevalidate);
+ insertQuery.bind(8, response.etag);
+ insertQuery.bind(9, response.expires);
+ insertQuery.bind(10, util::now());
if (response.noContent) {
- insert->bind(11, nullptr);
- insert->bind(12, false);
+ insertQuery.bind(11, nullptr);
+ insertQuery.bind(12, false);
} else {
- insert->bindBlob(11, data.data(), data.size(), false);
- insert->bind(12, compressed);
+ insertQuery.bindBlob(11, data.data(), data.size(), false);
+ insertQuery.bind(12, compressed);
}
- insert->run();
+ insertQuery.run();
transaction.commit();
return true;
}
std::vector<OfflineRegion> OfflineDatabase::listRegions() {
- // clang-format off
- Statement stmt = getStatement(
- "SELECT id, definition, description FROM regions");
- // clang-format on
+ mapbox::sqlite::Query query{ getStatement("SELECT id, definition, description FROM regions") };
std::vector<OfflineRegion> result;
- while (stmt->run()) {
+ while (query.run()) {
result.push_back(OfflineRegion(
- stmt->get<int64_t>(0),
- decodeOfflineRegionDefinition(stmt->get<std::string>(1)),
- stmt->get<std::vector<uint8_t>>(2)));
+ query.get<int64_t>(0),
+ decodeOfflineRegionDefinition(query.get<std::string>(1)),
+ query.get<std::vector<uint8_t>>(2)));
}
return result;
@@ -578,39 +564,37 @@ std::vector<OfflineRegion> OfflineDatabase::listRegions() {
OfflineRegion OfflineDatabase::createRegion(const OfflineRegionDefinition& definition,
const OfflineRegionMetadata& metadata) {
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
"INSERT INTO regions (definition, description) "
- "VALUES (?1, ?2) ");
+ "VALUES (?1, ?2) ") };
// clang-format on
- stmt->bind(1, encodeOfflineRegionDefinition(definition));
- stmt->bindBlob(2, metadata);
- stmt->run();
+ query.bind(1, encodeOfflineRegionDefinition(definition));
+ query.bindBlob(2, metadata);
+ query.run();
- return OfflineRegion(stmt->lastInsertRowId(), definition, metadata);
+ return OfflineRegion(query.lastInsertRowId(), definition, metadata);
}
OfflineRegionMetadata OfflineDatabase::updateMetadata(const int64_t regionID, const OfflineRegionMetadata& metadata) {
// clang-format off
- Statement stmt = getStatement(
- "UPDATE regions SET description = ?1"
- "WHERE id = ?2");
+ mapbox::sqlite::Query query{ getStatement(
+ "UPDATE regions SET description = ?1 "
+ "WHERE id = ?2") };
// clang-format on
- stmt->bindBlob(1, metadata);
- stmt->bind(2, regionID);
- stmt->run();
+ query.bindBlob(1, metadata);
+ query.bind(2, regionID);
+ query.run();
return metadata;
}
void OfflineDatabase::deleteRegion(OfflineRegion&& region) {
- // clang-format off
- Statement stmt = getStatement(
- "DELETE FROM regions WHERE id = ?");
- // clang-format on
-
- stmt->bind(1, region.getID());
- stmt->run();
+ {
+ mapbox::sqlite::Query query{ getStatement("DELETE FROM regions WHERE id = ?") };
+ query.bind(1, region.getID());
+ query.run();
+ }
evict(0);
db->exec("PRAGMA incremental_vacuum");
@@ -656,7 +640,7 @@ uint64_t OfflineDatabase::putRegionResource(int64_t regionID, const Resource& re
bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
if (resource.kind == Resource::Kind::Tile) {
// clang-format off
- Statement insert = getStatement(
+ mapbox::sqlite::Query insertQuery{ getStatement(
"INSERT OR IGNORE INTO region_tiles (region_id, tile_id) "
"SELECT ?1, tiles.id "
"FROM tiles "
@@ -664,24 +648,24 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
" AND pixel_ratio = ?3 "
" AND x = ?4 "
" AND y = ?5 "
- " AND z = ?6 ");
+ " AND z = ?6 ") };
// clang-format on
const Resource::TileData& tile = *resource.tileData;
- insert->bind(1, regionID);
- insert->bind(2, tile.urlTemplate);
- insert->bind(3, tile.pixelRatio);
- insert->bind(4, tile.x);
- insert->bind(5, tile.y);
- insert->bind(6, tile.z);
- insert->run();
-
- if (insert->changes() == 0) {
+ insertQuery.bind(1, regionID);
+ insertQuery.bind(2, tile.urlTemplate);
+ insertQuery.bind(3, tile.pixelRatio);
+ insertQuery.bind(4, tile.x);
+ insertQuery.bind(5, tile.y);
+ insertQuery.bind(6, tile.z);
+ insertQuery.run();
+
+ if (insertQuery.changes() == 0) {
return false;
}
// clang-format off
- Statement select = getStatement(
+ mapbox::sqlite::Query selectQuery{ getStatement(
"SELECT region_id "
"FROM region_tiles, tiles "
"WHERE region_id != ?1 "
@@ -690,58 +674,54 @@ bool OfflineDatabase::markUsed(int64_t regionID, const Resource& resource) {
" AND x = ?4 "
" AND y = ?5 "
" AND z = ?6 "
- "LIMIT 1 ");
+ "LIMIT 1 ") };
// clang-format on
- select->bind(1, regionID);
- select->bind(2, tile.urlTemplate);
- select->bind(3, tile.pixelRatio);
- select->bind(4, tile.x);
- select->bind(5, tile.y);
- select->bind(6, tile.z);
- return !select->run();
+ selectQuery.bind(1, regionID);
+ selectQuery.bind(2, tile.urlTemplate);
+ selectQuery.bind(3, tile.pixelRatio);
+ selectQuery.bind(4, tile.x);
+ selectQuery.bind(5, tile.y);
+ selectQuery.bind(6, tile.z);
+ return !selectQuery.run();
} else {
// clang-format off
- Statement insert = getStatement(
+ mapbox::sqlite::Query insertQuery{ getStatement(
"INSERT OR IGNORE INTO region_resources (region_id, resource_id) "
"SELECT ?1, resources.id "
"FROM resources "
- "WHERE resources.url = ?2 ");
+ "WHERE resources.url = ?2 ") };
// clang-format on
- insert->bind(1, regionID);
- insert->bind(2, resource.url);
- insert->run();
+ insertQuery.bind(1, regionID);
+ insertQuery.bind(2, resource.url);
+ insertQuery.run();
- if (insert->changes() == 0) {
+ if (insertQuery.changes() == 0) {
return false;
}
// clang-format off
- Statement select = getStatement(
+ mapbox::sqlite::Query selectQuery{ getStatement(
"SELECT region_id "
"FROM region_resources, resources "
"WHERE region_id != ?1 "
" AND resources.url = ?2 "
- "LIMIT 1 ");
+ "LIMIT 1 ") };
// clang-format on
- select->bind(1, regionID);
- select->bind(2, resource.url);
- return !select->run();
+ selectQuery.bind(1, regionID);
+ selectQuery.bind(2, resource.url);
+ return !selectQuery.run();
}
}
OfflineRegionDefinition OfflineDatabase::getRegionDefinition(int64_t regionID) {
- // clang-format off
- Statement stmt = getStatement(
- "SELECT definition FROM regions WHERE id = ?1");
- // clang-format on
-
- stmt->bind(1, regionID);
- stmt->run();
+ mapbox::sqlite::Query query{ getStatement("SELECT definition FROM regions WHERE id = ?1") };
+ query.bind(1, regionID);
+ query.run();
- return decodeOfflineRegionDefinition(stmt->get<std::string>(0));
+ return decodeOfflineRegionDefinition(query.get<std::string>(0));
}
OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID) {
@@ -760,35 +740,35 @@ OfflineRegionStatus OfflineDatabase::getRegionCompletedStatus(int64_t regionID)
std::pair<int64_t, int64_t> OfflineDatabase::getCompletedResourceCountAndSize(int64_t regionID) {
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
"SELECT COUNT(*), SUM(LENGTH(data)) "
"FROM region_resources, resources "
"WHERE region_id = ?1 "
- "AND resource_id = resources.id ");
+ "AND resource_id = resources.id ") };
// clang-format on
- stmt->bind(1, regionID);
- stmt->run();
- return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
+ query.bind(1, regionID);
+ query.run();
+ return { query.get<int64_t>(0), query.get<int64_t>(1) };
}
std::pair<int64_t, int64_t> OfflineDatabase::getCompletedTileCountAndSize(int64_t regionID) {
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
"SELECT COUNT(*), SUM(LENGTH(data)) "
"FROM region_tiles, tiles "
"WHERE region_id = ?1 "
- "AND tile_id = tiles.id ");
+ "AND tile_id = tiles.id ") };
// clang-format on
- stmt->bind(1, regionID);
- stmt->run();
- return { stmt->get<int64_t>(0), stmt->get<int64_t>(1) };
+ query.bind(1, regionID);
+ query.run();
+ return { query.get<int64_t>(0), query.get<int64_t>(1) };
}
template <class T>
-T OfflineDatabase::getPragma(const char * sql) {
- Statement stmt = getStatement(sql);
- stmt->run();
- return stmt->get<T>(0);
+T OfflineDatabase::getPragma(const char* sql) {
+ mapbox::sqlite::Query query{ getStatement(sql) };
+ query.run();
+ return query.get<T>(0);
}
// Remove least-recently used resources and tiles until the used database size,
@@ -813,7 +793,7 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
// size, and because pages can get fragmented on the database.
while (usedSize() + neededFreeSize + pageSize > maximumCacheSize) {
// clang-format off
- Statement accessedStmt = getStatement(
+ mapbox::sqlite::Query accessedQuery{ getStatement(
"SELECT max(accessed) "
"FROM ( "
" SELECT accessed "
@@ -829,16 +809,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
" WHERE tile_id IS NULL "
" ORDER BY accessed ASC LIMIT ?1 "
") "
- );
- accessedStmt->bind(1, 50);
+ ) };
+ accessedQuery.bind(1, 50);
// clang-format on
- if (!accessedStmt->run()) {
+ if (!accessedQuery.run()) {
return false;
}
- Timestamp accessed = accessedStmt->get<Timestamp>(0);
+ Timestamp accessed = accessedQuery.get<Timestamp>(0);
// clang-format off
- Statement stmt1 = getStatement(
+ mapbox::sqlite::Query resourceQuery{ getStatement(
"DELETE FROM resources "
"WHERE id IN ( "
" SELECT id FROM resources "
@@ -846,14 +826,14 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
" ON resource_id = resources.id "
" WHERE resource_id IS NULL "
" AND accessed <= ?1 "
- ") ");
+ ") ") };
// clang-format on
- stmt1->bind(1, accessed);
- stmt1->run();
- uint64_t changes1 = stmt1->changes();
+ resourceQuery.bind(1, accessed);
+ resourceQuery.run();
+ const uint64_t resourceChanges = resourceQuery.changes();
// clang-format off
- Statement stmt2 = getStatement(
+ mapbox::sqlite::Query tileQuery{ getStatement(
"DELETE FROM tiles "
"WHERE id IN ( "
" SELECT id FROM tiles "
@@ -861,16 +841,16 @@ bool OfflineDatabase::evict(uint64_t neededFreeSize) {
" ON tile_id = tiles.id "
" WHERE tile_id IS NULL "
" AND accessed <= ?1 "
- ") ");
+ ") ") };
// clang-format on
- stmt2->bind(1, accessed);
- stmt2->run();
- uint64_t changes2 = stmt2->changes();
+ tileQuery.bind(1, accessed);
+ tileQuery.run();
+ const uint64_t tileChanges = tileQuery.changes();
// The cached value of offlineTileCount does not need to be updated
// here because only non-offline tiles can be removed by eviction.
- if (changes1 == 0 && changes2 == 0) {
+ if (resourceChanges == 0 && tileChanges == 0) {
return false;
}
}
@@ -901,16 +881,16 @@ uint64_t OfflineDatabase::getOfflineMapboxTileCount() {
}
// clang-format off
- Statement stmt = getStatement(
+ mapbox::sqlite::Query query{ getStatement(
"SELECT COUNT(DISTINCT id) "
"FROM region_tiles, tiles "
"WHERE tile_id = tiles.id "
- "AND url_template LIKE 'mapbox://%' ");
+ "AND url_template LIKE 'mapbox://%' ") };
// clang-format on
- stmt->run();
+ query.run();
- offlineMapboxTileCount = stmt->get<int64_t>(0);
+ offlineMapboxTileCount = query.get<int64_t>(0);
return *offlineMapboxTileCount;
}
diff --git a/platform/default/mbgl/storage/offline_database.hpp b/platform/default/mbgl/storage/offline_database.hpp
index 91b544a9e0..9673ad8212 100644
--- a/platform/default/mbgl/storage/offline_database.hpp
+++ b/platform/default/mbgl/storage/offline_database.hpp
@@ -15,6 +15,7 @@ namespace mapbox {
namespace sqlite {
class Database;
class Statement;
+class Query;
} // namespace sqlite
} // namespace mapbox
@@ -66,20 +67,7 @@ private:
void migrateToVersion5();
void migrateToVersion6();
- class Statement {
- public:
- explicit Statement(mapbox::sqlite::Statement& stmt_) : stmt(stmt_) {}
- Statement(Statement&&) = default;
- Statement(const Statement&) = delete;
- ~Statement();
-
- mapbox::sqlite::Statement* operator->() { return &stmt; };
-
- private:
- mapbox::sqlite::Statement& stmt;
- };
-
- Statement getStatement(const char *);
+ mapbox::sqlite::Statement& getStatement(const char *);
optional<std::pair<Response, uint64_t>> getTile(const Resource::TileData&);
optional<int64_t> hasTile(const Resource::TileData&);
@@ -102,8 +90,8 @@ private:
std::pair<int64_t, int64_t> getCompletedTileCountAndSize(int64_t regionID);
const std::string path;
- std::unique_ptr<::mapbox::sqlite::Database> db;
- std::unordered_map<const char *, std::unique_ptr<::mapbox::sqlite::Statement>> statements;
+ std::unique_ptr<mapbox::sqlite::Database> db;
+ std::unordered_map<const char *, const std::unique_ptr<mapbox::sqlite::Statement>> statements;
template <class T>
T getPragma(const char *);
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/mbgl/util/default_styles.hpp b/platform/default/mbgl/util/default_styles.hpp
index 43dafb8083..13f08252a7 100644
--- a/platform/default/mbgl/util/default_styles.hpp
+++ b/platform/default/mbgl/util/default_styles.hpp
@@ -19,12 +19,9 @@ constexpr const DefaultStyle light = { "mapbox://styles/mapbox/light-
constexpr const DefaultStyle dark = { "mapbox://styles/mapbox/dark-v9", "Dark", 9 };
constexpr const DefaultStyle satellite = { "mapbox://styles/mapbox/satellite-v9", "Satellite", 9 };
constexpr const DefaultStyle satelliteStreets = { "mapbox://styles/mapbox/satellite-streets-v10", "Satellite Streets", 10 };
-constexpr const DefaultStyle trafficDay = { "mapbox://styles/mapbox/traffic-day-v2", "Traffic Day", 2 };
-constexpr const DefaultStyle trafficNight = { "mapbox://styles/mapbox/traffic-night-v2", "Traffic Night", 2 };
const DefaultStyle orderedStyles[] = {
streets, outdoors, light, dark, satellite, satelliteStreets,
- trafficDay, trafficNight,
};
const size_t numOrderedStyles = sizeof(orderedStyles) / sizeof(DefaultStyle);
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/default/sqlite3.cpp b/platform/default/sqlite3.cpp
index 2e08354fdf..8f9c34191f 100644
--- a/platform/default/sqlite3.cpp
+++ b/platform/default/sqlite3.cpp
@@ -69,14 +69,84 @@ public:
template <typename T>
using optional = std::experimental::optional<T>;
+static const char* codeToString(const int err) {
+ switch (err) {
+ case SQLITE_OK: return "SQLITE_OK";
+ case SQLITE_ERROR: return "SQLITE_ERROR";
+ case SQLITE_INTERNAL: return "SQLITE_INTERNAL";
+ case SQLITE_PERM: return "SQLITE_PERM";
+ case SQLITE_ABORT: return "SQLITE_ABORT";
+ case SQLITE_BUSY: return "SQLITE_BUSY";
+ case SQLITE_LOCKED: return "SQLITE_LOCKED";
+ case SQLITE_NOMEM: return "SQLITE_NOMEM";
+ case SQLITE_READONLY: return "SQLITE_READONLY";
+ case SQLITE_INTERRUPT: return "SQLITE_INTERRUPT";
+ case SQLITE_IOERR: return "SQLITE_IOERR";
+ case SQLITE_CORRUPT: return "SQLITE_CORRUPT";
+ case SQLITE_NOTFOUND: return "SQLITE_NOTFOUND";
+ case SQLITE_FULL: return "SQLITE_FULL";
+ case SQLITE_CANTOPEN: return "SQLITE_CANTOPEN";
+ case SQLITE_PROTOCOL: return "SQLITE_PROTOCOL";
+ case SQLITE_EMPTY: return "SQLITE_EMPTY";
+ case SQLITE_SCHEMA: return "SQLITE_SCHEMA";
+ case SQLITE_TOOBIG: return "SQLITE_TOOBIG";
+ case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT";
+ case SQLITE_MISMATCH: return "SQLITE_MISMATCH";
+ case SQLITE_MISUSE: return "SQLITE_MISUSE";
+ case SQLITE_NOLFS: return "SQLITE_NOLFS";
+ case SQLITE_AUTH: return "SQLITE_AUTH";
+ case SQLITE_FORMAT: return "SQLITE_FORMAT";
+ case SQLITE_RANGE: return "SQLITE_RANGE";
+ case SQLITE_NOTADB: return "SQLITE_NOTADB";
+ case SQLITE_NOTICE: return "SQLITE_NOTICE";
+ case SQLITE_WARNING: return "SQLITE_WARNING";
+ case SQLITE_ROW: return "SQLITE_ROW";
+ case SQLITE_DONE: return "SQLITE_DONE";
+ default: return "<unknown>";
+ }
+}
+
static void errorLogCallback(void *, const int err, const char *msg) {
- if (err == SQLITE_ERROR) {
- mbgl::Log::Error(mbgl::Event::Database, "%s (Code %i)", msg, err);
- } else if (err == SQLITE_WARNING) {
- mbgl::Log::Warning(mbgl::Event::Database, "%s (Code %i)", msg, err);
- } else {
- mbgl::Log::Info(mbgl::Event::Database, "%s (Code %i)", msg, err);
+ auto severity = mbgl::EventSeverity::Info;
+
+ switch (err) {
+ case SQLITE_ERROR: // Generic error
+ case SQLITE_INTERNAL: // Internal logic error in SQLite
+ case SQLITE_PERM: // Access permission denied
+ case SQLITE_ABORT: // Callback routine requested an abort
+ case SQLITE_BUSY: // The database file is locked
+ case SQLITE_LOCKED: // A table in the database is locked
+ case SQLITE_NOMEM: // A malloc() failed
+ case SQLITE_READONLY: // Attempt to write a readonly database
+ case SQLITE_INTERRUPT: // Operation terminated by sqlite3_interrupt(
+ case SQLITE_IOERR: // Some kind of disk I/O error occurred
+ case SQLITE_CORRUPT: // The database disk image is malformed
+ case SQLITE_NOTFOUND: // Unknown opcode in sqlite3_file_control()
+ case SQLITE_FULL: // Insertion failed because database is full
+ case SQLITE_CANTOPEN: // Unable to open the database file
+ case SQLITE_PROTOCOL: // Database lock protocol error
+ case SQLITE_EMPTY: // Internal use only
+ case SQLITE_SCHEMA: // The database schema changed
+ case SQLITE_TOOBIG: // String or BLOB exceeds size limit
+ case SQLITE_CONSTRAINT: // Abort due to constraint violation
+ case SQLITE_MISMATCH: // Data type mismatch
+ case SQLITE_MISUSE: // Library used incorrectly
+ case SQLITE_NOLFS: // Uses OS features not supported on host
+ case SQLITE_AUTH: // Authorization denied
+ case SQLITE_FORMAT: // Not used
+ case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range
+ case SQLITE_NOTADB: // File opened that is not a database file
+ severity = mbgl::EventSeverity::Error;
+ break;
+ case SQLITE_WARNING: // Warnings from sqlite3_log()
+ severity = mbgl::EventSeverity::Warning;
+ break;
+ case SQLITE_NOTICE: // Notifications from sqlite3_log()
+ default:
+ break;
}
+
+ mbgl::Log::Record(severity, mbgl::Event::Database, "%s (%s)", msg, codeToString(err));
}
const static bool sqliteVersionCheck __attribute__((unused)) = []() {
@@ -131,128 +201,137 @@ void Database::exec(const std::string &sql) {
}
}
-Statement Database::prepare(const char *query) {
- assert(impl);
- return Statement(this, query);
+Statement::Statement(Database& db, const char* sql)
+ : impl(std::make_unique<StatementImpl>(db.impl->db, sql)) {
}
-Statement::Statement(Database *db, const char *sql)
- : impl(std::make_unique<StatementImpl>(db->impl->db, sql))
-{
+Statement::~Statement() {
+#ifndef NDEBUG
+ // Crash if we're destructing this object while we know a Query object references this.
+ assert(!used);
+#endif
}
-Statement::Statement(Statement &&other) {
- *this = std::move(other);
-}
+Query::Query(Statement& stmt_) : stmt(stmt_) {
+ assert(stmt.impl);
-Statement &Statement::operator=(Statement &&other) {
- std::swap(impl, other.impl);
- return *this;
+#ifndef NDEBUG
+ assert(!stmt.used);
+ stmt.used = true;
+#endif
}
-Statement::~Statement() = default;
+Query::~Query() {
+ reset();
+ clearBindings();
-template <> void Statement::bind(int offset, std::nullptr_t) {
- assert(impl);
- impl->check(sqlite3_bind_null(impl->stmt, offset));
+#ifndef NDEBUG
+ stmt.used = false;
+#endif
}
-template <> void Statement::bind(int offset, int8_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, std::nullptr_t) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_null(stmt.impl->stmt, offset));
}
-template <> void Statement::bind(int offset, int16_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, int8_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, int32_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, int16_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, int64_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, int32_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, uint8_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, int64_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, uint16_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, uint8_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, uint32_t value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, value));
+template <> void Query::bind(int offset, uint16_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, float value) {
- assert(impl);
- impl->check(sqlite3_bind_double(impl->stmt, offset, value));
+template <> void Query::bind(int offset, uint32_t value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, double value) {
- assert(impl);
- impl->check(sqlite3_bind_double(impl->stmt, offset, value));
+template <> void Query::bind(int offset, float value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, bool value) {
- assert(impl);
- impl->check(sqlite3_bind_int(impl->stmt, offset, value));
+template <> void Query::bind(int offset, double value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_double(stmt.impl->stmt, offset, value));
}
-template <> void Statement::bind(int offset, const char *value) {
- assert(impl);
- impl->check(sqlite3_bind_text(impl->stmt, offset, value, -1, SQLITE_STATIC));
+template <> void Query::bind(int offset, bool value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int(stmt.impl->stmt, offset, value));
}
-// We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they
-// was introduced in SQLite 3.8.7, and we need to support earlier versions:
-// iOS 8.0: 3.7.13
-// iOS 8.2: 3.8.5
-// According to http://stackoverflow.com/questions/14288128/what-version-of-sqlite-does-ios-provide,
-// the first iOS version with 3.8.7+ was 9.0, with 3.8.8.
+template <> void Query::bind(int offset, const char *value) {
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, -1, SQLITE_STATIC));
+}
-void Statement::bind(int offset, const char * value, std::size_t length, bool retain) {
- assert(impl);
+// We currently cannot use sqlite3_bind_blob64 / sqlite3_bind_text64 because they
+// were introduced in SQLite 3.8.7, and we need to support earlier versions:
+// Android 11: 3.7
+// Android 21: 3.8
+// Android 24: 3.9
+// Per https://developer.android.com/reference/android/database/sqlite/package-summary.
+// The first iOS version with 3.8.7+ was 9.0, with 3.8.8.
+
+void Query::bind(int offset, const char * value, std::size_t length, bool retain) {
+ assert(stmt.impl);
if (length > std::numeric_limits<int>::max()) {
throw std::range_error("value too long for sqlite3_bind_text");
}
- impl->check(sqlite3_bind_text(impl->stmt, offset, value, int(length),
+ stmt.impl->check(sqlite3_bind_text(stmt.impl->stmt, offset, value, int(length),
retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
}
-void Statement::bind(int offset, const std::string& value, bool retain) {
+void Query::bind(int offset, const std::string& value, bool retain) {
bind(offset, value.data(), value.size(), retain);
}
-void Statement::bindBlob(int offset, const void * value, std::size_t length, bool retain) {
- assert(impl);
+void Query::bindBlob(int offset, const void * value, std::size_t length, bool retain) {
+ assert(stmt.impl);
if (length > std::numeric_limits<int>::max()) {
throw std::range_error("value too long for sqlite3_bind_text");
}
- impl->check(sqlite3_bind_blob(impl->stmt, offset, value, int(length),
+ stmt.impl->check(sqlite3_bind_blob(stmt.impl->stmt, offset, value, int(length),
retain ? SQLITE_TRANSIENT : SQLITE_STATIC));
}
-void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) {
+void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) {
bindBlob(offset, value.data(), value.size(), retain);
}
template <>
-void Statement::bind(
+void Query::bind(
int offset, std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds> value) {
- assert(impl);
- impl->check(sqlite3_bind_int64(impl->stmt, offset, std::chrono::system_clock::to_time_t(value)));
+ assert(stmt.impl);
+ stmt.impl->check(sqlite3_bind_int64(stmt.impl->stmt, offset, std::chrono::system_clock::to_time_t(value)));
}
-template <> void Statement::bind(int offset, optional<std::string> value) {
+template <> void Query::bind(int offset, optional<std::string> value) {
if (!value) {
bind(offset, nullptr);
} else {
@@ -261,7 +340,7 @@ template <> void Statement::bind(int offset, optional<std::string> value) {
}
template <>
-void Statement::bind(
+void Query::bind(
int offset,
optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>> value) {
if (!value) {
@@ -271,86 +350,86 @@ void Statement::bind(
}
}
-bool Statement::run() {
- assert(impl);
- const int err = sqlite3_step(impl->stmt);
- impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(impl->stmt));
- impl->changes = sqlite3_changes(sqlite3_db_handle(impl->stmt));
+bool Query::run() {
+ assert(stmt.impl);
+ const int err = sqlite3_step(stmt.impl->stmt);
+ stmt.impl->lastInsertRowId = sqlite3_last_insert_rowid(sqlite3_db_handle(stmt.impl->stmt));
+ stmt.impl->changes = sqlite3_changes(sqlite3_db_handle(stmt.impl->stmt));
if (err == SQLITE_DONE) {
return false;
} else if (err == SQLITE_ROW) {
return true;
} else if (err != SQLITE_OK) {
- throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(impl->stmt)) };
+ throw Exception { err, sqlite3_errmsg(sqlite3_db_handle(stmt.impl->stmt)) };
} else {
return false;
}
}
-template <> bool Statement::get(int offset) {
- assert(impl);
- return sqlite3_column_int(impl->stmt, offset);
+template <> bool Query::get(int offset) {
+ assert(stmt.impl);
+ return sqlite3_column_int(stmt.impl->stmt, offset);
}
-template <> int Statement::get(int offset) {
- assert(impl);
- return sqlite3_column_int(impl->stmt, offset);
+template <> int Query::get(int offset) {
+ assert(stmt.impl);
+ return sqlite3_column_int(stmt.impl->stmt, offset);
}
-template <> int64_t Statement::get(int offset) {
- assert(impl);
- return sqlite3_column_int64(impl->stmt, offset);
+template <> int64_t Query::get(int offset) {
+ assert(stmt.impl);
+ return sqlite3_column_int64(stmt.impl->stmt, offset);
}
-template <> double Statement::get(int offset) {
- assert(impl);
- return sqlite3_column_double(impl->stmt, offset);
+template <> double Query::get(int offset) {
+ assert(stmt.impl);
+ return sqlite3_column_double(stmt.impl->stmt, offset);
}
-template <> std::string Statement::get(int offset) {
- assert(impl);
+template <> std::string Query::get(int offset) {
+ assert(stmt.impl);
return {
- reinterpret_cast<const char *>(sqlite3_column_blob(impl->stmt, offset)),
- size_t(sqlite3_column_bytes(impl->stmt, offset))
+ reinterpret_cast<const char *>(sqlite3_column_blob(stmt.impl->stmt, offset)),
+ size_t(sqlite3_column_bytes(stmt.impl->stmt, offset))
};
}
-template <> std::vector<uint8_t> Statement::get(int offset) {
- assert(impl);
- const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(impl->stmt, offset));
- const uint8_t* end = begin + sqlite3_column_bytes(impl->stmt, offset);
+template <> std::vector<uint8_t> Query::get(int offset) {
+ assert(stmt.impl);
+ const auto* begin = reinterpret_cast<const uint8_t*>(sqlite3_column_blob(stmt.impl->stmt, offset));
+ const uint8_t* end = begin + sqlite3_column_bytes(stmt.impl->stmt, offset);
return { begin, end };
}
template <>
std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
-Statement::get(int offset) {
- assert(impl);
+Query::get(int offset) {
+ assert(stmt.impl);
return std::chrono::time_point_cast<std::chrono::seconds>(
- std::chrono::system_clock::from_time_t(sqlite3_column_int64(impl->stmt, offset)));
+ std::chrono::system_clock::from_time_t(sqlite3_column_int64(stmt.impl->stmt, offset)));
}
-template <> optional<int64_t> Statement::get(int offset) {
- assert(impl);
- if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) {
+template <> optional<int64_t> Query::get(int offset) {
+ assert(stmt.impl);
+ if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
return optional<int64_t>();
} else {
return get<int64_t>(offset);
}
}
-template <> optional<double> Statement::get(int offset) {
- assert(impl);
- if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) {
+template <> optional<double> Query::get(int offset) {
+ assert(stmt.impl);
+ if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
return optional<double>();
} else {
return get<double>(offset);
}
}
-template <> optional<std::string> Statement::get(int offset) {
- assert(impl);
- if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) {
+template <> optional<std::string> Query::get(int offset) {
+ assert(stmt.impl);
+ if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
return optional<std::string>();
} else {
return get<std::string>(offset);
@@ -359,9 +438,9 @@ template <> optional<std::string> Statement::get(int offset) {
template <>
optional<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>
-Statement::get(int offset) {
- assert(impl);
- if (sqlite3_column_type(impl->stmt, offset) == SQLITE_NULL) {
+Query::get(int offset) {
+ assert(stmt.impl);
+ if (sqlite3_column_type(stmt.impl->stmt, offset) == SQLITE_NULL) {
return {};
} else {
return get<std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>>(
@@ -369,24 +448,24 @@ Statement::get(int offset) {
}
}
-void Statement::reset() {
- assert(impl);
- sqlite3_reset(impl->stmt);
+void Query::reset() {
+ assert(stmt.impl);
+ sqlite3_reset(stmt.impl->stmt);
}
-void Statement::clearBindings() {
- assert(impl);
- sqlite3_clear_bindings(impl->stmt);
+void Query::clearBindings() {
+ assert(stmt.impl);
+ sqlite3_clear_bindings(stmt.impl->stmt);
}
-int64_t Statement::lastInsertRowId() const {
- assert(impl);
- return impl->lastInsertRowId;
+int64_t Query::lastInsertRowId() const {
+ assert(stmt.impl);
+ return stmt.impl->lastInsertRowId;
}
-uint64_t Statement::changes() const {
- assert(impl);
- auto changes_ = impl->changes;
+uint64_t Query::changes() const {
+ assert(stmt.impl);
+ auto changes_ = stmt.impl->changes;
return (changes_ < 0 ? 0 : changes_);
}
diff --git a/platform/default/sqlite3.hpp b/platform/default/sqlite3.hpp
index 82e3ceff6d..20d09b550c 100644
--- a/platform/default/sqlite3.hpp
+++ b/platform/default/sqlite3.hpp
@@ -19,21 +19,55 @@ enum OpenFlag : int {
PrivateCache = 0x00040000,
};
-struct Exception : std::runtime_error {
- enum Code : int {
- OK = 0,
- CANTOPEN = 14,
- NOTADB = 26
- };
+enum class ResultCode : int {
+ OK = 0,
+ Error = 1,
+ Internal = 2,
+ Perm = 3,
+ Abort = 4,
+ Busy = 5,
+ Locked = 6,
+ NoMem = 7,
+ ReadOnly = 8,
+ Interrupt = 9,
+ IOErr = 10,
+ Corrupt = 11,
+ NotFound = 12,
+ Full = 13,
+ CantOpen = 14,
+ Protocol = 15,
+ Schema = 17,
+ TooBig = 18,
+ Constraint = 19,
+ Mismatch = 20,
+ Misuse = 21,
+ NoLFS = 22,
+ Auth = 23,
+ Range = 25,
+ NotADB = 26
+};
- Exception(int err, const char *msg) : std::runtime_error(msg), code(err) {}
- Exception(int err, const std::string& msg) : std::runtime_error(msg), code(err) {}
- const int code = OK;
+class Exception : public std::runtime_error {
+public:
+ Exception(int err, const char* msg)
+ : std::runtime_error(msg), code(static_cast<ResultCode>(err)) {
+ }
+ Exception(ResultCode err, const char* msg)
+ : std::runtime_error(msg), code(err) {
+ }
+ Exception(int err, const std::string& msg)
+ : std::runtime_error(msg), code(static_cast<ResultCode>(err)) {
+ }
+ Exception(ResultCode err, const std::string& msg)
+ : std::runtime_error(msg), code(err) {
+ }
+ const ResultCode code = ResultCode::OK;
};
class DatabaseImpl;
class Statement;
class StatementImpl;
+class Query;
class Database {
private:
@@ -48,7 +82,6 @@ public:
void setBusyTimeout(std::chrono::milliseconds);
void exec(const std::string &sql);
- Statement prepare(const char *query);
private:
std::unique_ptr<DatabaseImpl> impl;
@@ -56,28 +89,54 @@ private:
friend class Statement;
};
+// A Statement object represents a prepared statement that can be run repeatedly run with a Query object.
class Statement {
+public:
+ Statement(Database& db, const char* sql);
+ Statement(const Statement&) = delete;
+ Statement(Statement&&) = delete;
+ Statement& operator=(const Statement&) = delete;
+ Statement& operator=(Statement&&) = delete;
+ ~Statement();
+
+ friend class Query;
+
private:
- Statement(const Statement &) = delete;
- Statement &operator=(const Statement &) = delete;
+ std::unique_ptr<StatementImpl> impl;
+
+#ifndef NDEBUG
+ // This flag stores whether there exists a Query object that uses this prepared statement.
+ // There may only be one Query object at a time. Statement objects must outlive Query objects.
+ // While a Query object exists, a Statement object may not be moved or deleted.
+ bool used = false;
+#endif
+};
+// A Query object is used to run a database query with a prepared statement (stored in a Statement
+// object). There may only exist one Query object per Statement object. Query objects are designed
+// to be constructed and destroyed frequently.
+class Query {
public:
- Statement(Database *db, const char *sql);
- Statement(Statement &&);
- ~Statement();
- Statement &operator=(Statement &&);
+ Query(Statement&);
+ Query(const Query&) = delete;
+ Query(Query&&) = delete;
+ Query& operator=(const Query&) = delete;
+ Query& operator=(Query&&) = delete;
+ ~Query();
- template <typename T> void bind(int offset, T value);
+ template <typename T>
+ void bind(int offset, T value);
// Text
- void bind(int offset, const char *, std::size_t length, bool retain = true);
+ void bind(int offset, const char*, std::size_t length, bool retain = true);
void bind(int offset, const std::string&, bool retain = true);
// Blob
- void bindBlob(int offset, const void *, std::size_t length, bool retain = true);
+ void bindBlob(int offset, const void*, std::size_t length, bool retain = true);
void bindBlob(int offset, const std::vector<uint8_t>&, bool retain = true);
- template <typename T> T get(int offset);
+ template <typename T>
+ T get(int offset);
bool run();
void reset();
@@ -87,7 +146,7 @@ public:
uint64_t changes() const;
private:
- std::unique_ptr<StatementImpl> impl;
+ Statement& stmt;
};
class Transaction {
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 1b0806b689..e87c8d6ad1 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -2,6 +2,16 @@
Mapbox welcomes participation and contributions from everyone. Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) to get started.
+## master
+
+### Packaging
+
+* The minimum deployment target for this SDK is now iOS 9.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776))
+
+### Style layers
+
+* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748))
+
## 3.7.8 - May 7, 2018
* Improved compatibility with Mapbox China APIs. ([#11845](https://github.com/mapbox/mapbox-gl-native/pull/11845))
diff --git a/platform/ios/DEVELOPING.md b/platform/ios/DEVELOPING.md
index 40d45e2e56..34388c2589 100644
--- a/platform/ios/DEVELOPING.md
+++ b/platform/ios/DEVELOPING.md
@@ -4,14 +4,10 @@ This document explains how to build the Mapbox Maps SDK for iOS from source. It
## Requirements
-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.
+See the "Requirements" section in [INSTALL.md](INSTALL.md).
## Building the SDK
-Make sure that you have the [core dependencies](../../INSTALL.md) installed.
-
Create and open an Xcode workspace that includes both the SDK source and some Objective-C test applications by running:
```bash
@@ -30,7 +26,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 +48,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.
@@ -139,6 +135,8 @@ To add an example code listing to the documentation for a class or class member:
to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift).
Wrap the code you’d like to appear in the documentation within
`//#-example-code` and `//#-end-example-code` comments.
+1. If the header doesn’t already have an example code listing, add the path to
+ the header to platform/darwin/scripts/update-examples.list.
1. Insert the code listings into the headers:
```bash
@@ -168,5 +166,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..3c79e4dcf8 100644
--- a/platform/ios/INSTALL.md
+++ b/platform/ios/INSTALL.md
@@ -4,29 +4,50 @@ This document explains how to build a development version of Mapbox Maps SDK for
### Requirements
-The Mapbox Maps SDK for iOS is intended to run on iOS 8.0 and above on the following devices:
+The Mapbox Maps SDK for iOS is intended to run on iOS 9.0 and above on the following devices:
* iPhone 4s and above (5, 5c, 5s, 6, 6 Plus, 7, 7 Plus, 8, 8 Plus, X)
* 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.
-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.
+Note that 32-bit simulators (such as the iPhone 5 or iPad 2) are not supported.
-### Building the SDK
+The Mapbox Maps SDK for iOS requires:
-1. [Install core dependencies](../../INSTALL.md).
+* Xcode 9.1 or higher to compile from source
+* Xcode 8.0 or higher to integrate the compiled framework into an application
-1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation:
+Before building, follow these steps to install prerequisites:
+1. Install [Xcode](https://developer.apple.com/xcode/)
+1. Launch Xcode and install any updates
+1. Install [Homebrew](http://brew.sh)
+1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org):
+ ```
+ brew install node cmake ccache
+ ```
+1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds):
+ ```
+ [sudo] gem install xcpretty
+ ```
+1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation:
```
[sudo] gem install jazzy
```
-1. Run `make ipackage`. The packaging script will produce a `build/ios/pkg/` folder containing:
+### Building the SDK
+
+1. Clone the git repository:
+ ```
+ git clone https://github.com/mapbox/mapbox-gl-native.git
+ cd mapbox-gl-native
+ ```
+ Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command,
+ but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to
+ this repository.
+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 +81,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..4f42c5a13f
--- /dev/null
+++ b/platform/ios/Integration Tests/MBGLIntegrationTests.m
@@ -0,0 +1,202 @@
+#import "MGLMapViewIntegrationTest.h"
+
+@interface MBGLIntegrationTests : MGLMapViewIntegrationTest
+@end
+
+@implementation MBGLIntegrationTests
+
+#pragma mark - Tests
+
+- (void)waitForMapViewToBeRendered {
+ [self waitForMapViewToBeRenderedWithTimeout:1];
+}
+
+// This test does not strictly need to be in this test file/target. Including here for convenience.
+- (void)testOpenGLLayerDoesNotLeakWhenCreatedAndDestroyedWithoutAddingToStyle {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ __weak id weakLayer = layer;
+ layer = nil;
+
+ XCTAssertNil(weakLayer);
+}
+
+- (void)testAddingRemovingOpenGLLayerWithoutRendering {
+ XCTAssertNotNil(self.style);
+
+ void(^addRemoveGLLayer)(void) = ^{
+ __weak id weakLayer = nil;
+
+ @autoreleasepool {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ [self.style insertLayer:layer atIndex:0];
+ weakLayer = layer;
+
+ // Nil the layer prior to remove to ensure it's being retained
+ layer = nil;
+ [self.style removeLayer:weakLayer];
+ }
+
+ XCTAssertNil(weakLayer);
+ };
+
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+}
+
+- (void)testReusingOpenGLLayerIdentifier {
+ __weak MGLOpenGLStyleLayer *weakLayer2;
+
+ @autoreleasepool {
+ MGLOpenGLStyleLayer *layer1 = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ [self.style insertLayer:layer1 atIndex:0];
+ [self waitForMapViewToBeRendered];
+ [self.style removeLayer:layer1];
+
+ MGLOpenGLStyleLayer *layer2 = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ weakLayer2 = layer2;
+
+ XCTAssertNotNil(layer2);
+ XCTAssert(layer1 != layer2);
+
+ [self.style insertLayer:layer2 atIndex:0];
+ [self waitForMapViewToBeRendered];
+ [self.style removeLayer:layer2];
+
+ XCTAssertNil([layer1 style]);
+ XCTAssertNil([layer2 style]);
+ }
+
+ // At this point, layer2 (and layer1) should still be around,
+ // since the render process needs to keep a reference to them.
+ XCTAssertNotNil(weakLayer2);
+
+ // Let render loop run enough to release the layers
+ [self waitForMapViewToBeRendered];
+ XCTAssertNil(weakLayer2);
+}
+
+- (void)testAddingRemovingOpenGLLayer {
+ XCTAssertNotNil(self.style);
+
+ void(^addRemoveGLLayer)(void) = ^{
+
+ __weak id retrievedLayer = nil;
+
+ @autoreleasepool {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ [self.style insertLayer:layer atIndex:0];
+ layer = nil;
+
+ [self waitForMapViewToBeRendered];
+
+ retrievedLayer = [self.style layerWithIdentifier:@"gl-layer"];
+ XCTAssertNotNil(retrievedLayer);
+
+ [self.style removeLayer:retrievedLayer];
+ [self waitForMapViewToBeRendered];
+ }
+
+ XCTAssertNil(retrievedLayer);
+ };
+
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+ addRemoveGLLayer();
+}
+
+- (void)testReusingOpenGLLayer {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ [self.style insertLayer:layer atIndex:0];
+ [self waitForMapViewToBeRendered];
+
+ [self.style removeLayer:layer];
+ [self waitForMapViewToBeRendered];
+
+ [self.style insertLayer:layer atIndex:0];
+ [self waitForMapViewToBeRendered];
+
+ [self.style removeLayer:layer];
+ [self waitForMapViewToBeRendered];
+}
+
+- (void)testOpenGLLayerDoesNotLeakWhenRemovedFromStyle {
+ __weak id weakLayer;
+ @autoreleasepool {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ weakLayer = layer;
+ [self.style insertLayer:layer atIndex:0];
+ layer = nil;
+
+ [self waitForMapViewToBeRendered];
+ [self.style removeLayer:[self.style layerWithIdentifier:@"gl-layer"]];
+ }
+
+ MGLStyleLayer *layer2 = weakLayer;
+
+ XCTAssertNotNil(weakLayer);
+ [self waitForMapViewToBeRendered];
+
+ layer2 = nil;
+ XCTAssertNil(weakLayer);
+}
+
+- (void)testOpenGLLayerDoesNotLeakWhenStyleChanged {
+ __weak MGLOpenGLStyleLayer *weakLayer;
+
+ @autoreleasepool {
+ {
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ weakLayer = layer;
+ [self.style insertLayer:layer atIndex:0];
+ layer = nil;
+ }
+ }
+
+ XCTAssertNotNil(weakLayer);
+
+ [self waitForMapViewToBeRendered];
+
+ MGLStyleLayer *layer2 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
+
+ NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
+ self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
+ [self.mapView setStyleURL:styleURL];
+ [self waitForExpectations:@[self.styleLoadingExpectation] timeout:10];
+
+ // At this point the C++ CustomLayer will have been destroyed, and the rawLayer pointer has been NULLed
+ XCTAssert(weakLayer == layer2);
+ XCTAssertNotNil(weakLayer);
+
+ // Asking the style for the layer should return nil
+ MGLStyleLayer *layer3 = [self.mapView.style layerWithIdentifier:@"gl-layer"];
+ XCTAssertNil(layer3);
+}
+
+
+- (void)testOpenGLLayerDoesNotLeakWhenMapViewDeallocs {
+ __weak id weakLayer;
+
+ @autoreleasepool {
+
+ NSURL *styleURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"];
+ MGLMapView *mapView2 = [[MGLMapView alloc] initWithFrame:UIScreen.mainScreen.bounds styleURL:styleURL];
+ mapView2.delegate = self;
+
+ XCTAssertNil(mapView2.style);
+
+ self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+
+ MGLOpenGLStyleLayer *layer = [[MGLOpenGLStyleLayer alloc] initWithIdentifier:@"gl-layer"];
+ weakLayer = layer;
+ [mapView2.style insertLayer:layer atIndex:0];
+ layer = nil;
+
+ [self waitForMapViewToBeRendered];
+ }
+ XCTAssertNil(weakLayer);
+}
+
+@end
+
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
new file mode 100644
index 0000000000..ab5d2cc46f
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.h
@@ -0,0 +1,20 @@
+#import <XCTest/XCTest.h>
+#import <Mapbox/Mapbox.h>
+
+#define TestFailWithSelf(myself, ...) \
+ _XCTPrimitiveFail(myself, __VA_ARGS__)
+
+@interface MGLMapViewIntegrationTest : XCTestCase <MGLMapViewDelegate>
+@property (nonatomic) MGLMapView *mapView;
+@property (nonatomic) MGLStyle *style;
+@property (nonatomic) XCTestExpectation *styleLoadingExpectation;
+@property (nonatomic) XCTestExpectation *renderFinishedExpectation;
+@property (nonatomic) void (^regionDidChange)(MGLMapView *mapView, BOOL animated);
+@property (nonatomic) void (^regionIsChanging)(MGLMapView *mapView);
+
+
+
+// Utility methods
+- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout;
+- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout;
+@end
diff --git a/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
new file mode 100644
index 0000000000..fc3229c83b
--- /dev/null
+++ b/platform/ios/Integration Tests/MGLMapViewIntegrationTest.m
@@ -0,0 +1,79 @@
+#import "MGLMapViewIntegrationTest.h"
+
+@implementation MGLMapViewIntegrationTest
+
+- (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;
+
+ 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];
+
+ if (!self.mapView.style) {
+ [self waitForMapViewToFinishLoadingStyleWithTimeout:1];
+ }
+}
+
+- (void)tearDown {
+ self.styleLoadingExpectation = nil;
+ self.renderFinishedExpectation = nil;
+ self.mapView = nil;
+ self.style = nil;
+
+ [super tearDown];
+}
+
+#pragma mark - MGLMapViewDelegate
+
+- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
+ XCTAssertNotNil(mapView.style);
+ XCTAssertEqual(mapView.style, style);
+
+ [self.styleLoadingExpectation fulfill];
+}
+
+- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(__unused BOOL)fullyRendered {
+ [self.renderFinishedExpectation fulfill];
+ self.renderFinishedExpectation = nil;
+}
+
+- (void)mapView:(MGLMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
+ if (self.regionDidChange) {
+ self.regionDidChange(mapView, animated);
+ }
+}
+
+- (void)mapViewRegionIsChanging:(MGLMapView *)mapView {
+ if (self.regionIsChanging) {
+ self.regionIsChanging(mapView);
+ }
+}
+
+#pragma mark - Utilities
+
+- (void)waitForMapViewToFinishLoadingStyleWithTimeout:(NSTimeInterval)timeout {
+ XCTAssertNil(self.styleLoadingExpectation);
+ self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
+ [self waitForExpectations:@[self.styleLoadingExpectation] timeout:timeout];
+}
+
+- (void)waitForMapViewToBeRenderedWithTimeout:(NSTimeInterval)timeout {
+ XCTAssertNil(self.renderFinishedExpectation);
+ [self.mapView setNeedsDisplay];
+ self.renderFinishedExpectation = [self expectationWithDescription:@"Map view should be rendered"];
+ [self waitForExpectations:@[self.renderFinishedExpectation] timeout:timeout];
+}
+
+- (MGLStyle *)style {
+ return self.mapView.style;
+}
+
+@end
diff --git a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
index 265331791a..e07178311b 100644
--- a/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-nightly-dynamic.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.7.8'
+ version = '4.0.0'
m.name = 'Mapbox-iOS-SDK-nightly-dynamic'
m.version = "#{version}-nightly"
@@ -20,7 +20,7 @@ Pod::Spec.new do |m|
}
m.platform = :ios
- m.ios.deployment_target = '8.0'
+ m.ios.deployment_target = '9.0'
m.requires_arc = true
diff --git a/platform/ios/Mapbox-iOS-SDK-static-part.podspec b/platform/ios/Mapbox-iOS-SDK-static-part.podspec
index b2a114f9d2..bd98bb7272 100644
--- a/platform/ios/Mapbox-iOS-SDK-static-part.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-static-part.podspec
@@ -1,4 +1,4 @@
- m.ios.deployment_target = '8.0'
+ m.ios.deployment_target = '9.0'
m.requires_arc = true
diff --git a/platform/ios/Mapbox-iOS-SDK-symbols.podspec b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
index 3e3311f912..fbf65f0b24 100644
--- a/platform/ios/Mapbox-iOS-SDK-symbols.podspec
+++ b/platform/ios/Mapbox-iOS-SDK-symbols.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.7.8'
+ version = '4.0.0'
m.name = 'Mapbox-iOS-SDK-symbols'
m.version = "#{version}-symbols"
@@ -20,7 +20,7 @@ Pod::Spec.new do |m|
}
m.platform = :ios
- m.ios.deployment_target = '8.0'
+ m.ios.deployment_target = '9.0'
m.requires_arc = true
diff --git a/platform/ios/Mapbox-iOS-SDK.podspec b/platform/ios/Mapbox-iOS-SDK.podspec
index 6eaac7b73b..5a8e913dd9 100644
--- a/platform/ios/Mapbox-iOS-SDK.podspec
+++ b/platform/ios/Mapbox-iOS-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '3.7.8'
+ version = '4.0.0'
m.name = 'Mapbox-iOS-SDK'
m.version = version
@@ -20,7 +20,7 @@ Pod::Spec.new do |m|
}
m.platform = :ios
- m.ios.deployment_target = '8.0'
+ m.ios.deployment_target = '9.0'
m.requires_arc = true
diff --git a/platform/ios/Mapbox.playground/Contents.swift b/platform/ios/Mapbox.playground/Contents.swift
index 1f368be73b..f31c9b6171 100644
--- a/platform/ios/Mapbox.playground/Contents.swift
+++ b/platform/ios/Mapbox.playground/Contents.swift
@@ -1,9 +1,5 @@
import UIKit
-#if swift(>=3)
- import PlaygroundSupport
-#else
- import XCPlayground
-#endif
+import PlaygroundSupport
import Mapbox
let width: CGFloat = 700
@@ -11,13 +7,9 @@ let height: CGFloat = 800
class Responder: NSObject {
var mapView: MGLMapView?
- func togglePitch(sender: UISwitch) {
+ @objc func togglePitch(sender: UISwitch) {
let camera = mapView!.camera
- #if swift(>=3)
- camera.pitch = sender.isOn ? 60 : 0
- #else
- camera.pitch = sender.on ? 60 : 0
- #endif
+ camera.pitch = sender.isOn ? 60 : 0
mapView!.setCamera(camera, animated: false)
}
}
@@ -26,11 +18,7 @@ class Responder: NSObject {
let panelWidth: CGFloat = 200
let panel = UIView(frame: CGRect(x: width - panelWidth, y: 0, width: 200, height: 100))
panel.alpha = 0.8
-#if swift(>=3)
- panel.backgroundColor = .white
-#else
- panel.backgroundColor = UIColor.whiteColor()
-#endif
+panel.backgroundColor = .white
// Delete markers
let deleteSwitchLabel = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 30))
@@ -53,11 +41,7 @@ let pitchLabel = UILabel(frame: CGRect(x: 0, y: 60, width: 100, height: 30))
pitchLabel.text = "Pitch"
let pitchSwitch = UISwitch(frame: CGRect(x: panelWidth-panelWidth / 2.0, y: 60, width: 100, height: 50))
let responder = Responder()
-#if swift(>=3)
- pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(sender:)), for: .valueChanged)
-#else
- pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(_:)), forControlEvents: .ValueChanged)
-#endif
+pitchSwitch.addTarget(responder, action: #selector(responder.togglePitch(sender:)), for: .valueChanged)
panel.addSubview(pitchLabel)
panel.addSubview(pitchSwitch)
@@ -67,16 +51,12 @@ panel.addSubview(pitchSwitch)
Put your access token into a plain text file called `token`. Then select the “token” placeholder below, go to Editor ‣ Insert File Literal, and select the `token` file.
*/
var accessToken = try String(contentsOfURL: <#token#>)
-MGLAccountManager.setAccessToken(accessToken)
+MGLAccountManager.accessToken = accessToken
class PlaygroundAnnotationView: MGLAnnotationView {
override func prepareForReuse() {
- #if swift(>=3)
- isHidden = hideMarkerSwitchView.isOn
- #else
- hidden = hideMarkerSwitchView.on
- #endif
+ isHidden = hideMarkerSwitchView.isOn
}
}
@@ -86,8 +66,7 @@ class PlaygroundAnnotationView: MGLAnnotationView {
class MapDelegate: NSObject, MGLMapViewDelegate {
var annotationViewByAnnotation = [MGLPointAnnotation: PlaygroundAnnotationView]()
-
- #if swift(>=3)
+
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "annotation") as? PlaygroundAnnotationView
@@ -109,31 +88,7 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
return annotationView
}
- #else
- func mapView(mapView: MGLMapView, viewForAnnotation annotation: MGLAnnotation) -> MGLAnnotationView? {
-
- var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("annotation") as? PlaygroundAnnotationView
-
- if (annotationView == nil) {
- let av = PlaygroundAnnotationView(reuseIdentifier: "annotation")
- av.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
- av.centerOffset = CGVector(dx: -15, dy: -15)
- let centerView = UIView(frame: CGRectInset(av.bounds, 3, 3))
- centerView.backgroundColor = UIColor.whiteColor()
- av.addSubview(centerView)
- av.backgroundColor = UIColor.purpleColor()
- annotationView = av
- } else {
- annotationView!.subviews.first?.backgroundColor = UIColor.greenColor()
- }
-
- annotationViewByAnnotation[annotation as! MGLPointAnnotation] = annotationView
-
- return annotationView
- }
- #endif
-
- #if swift(>=3)
+
func mapView(_ mapView: MGLMapView, didSelect annotation: MGLAnnotation) {
let pointAnnotation = annotation as! MGLPointAnnotation
let annotationView: PlaygroundAnnotationView = annotationViewByAnnotation[pointAnnotation]!
@@ -159,48 +114,14 @@ class MapDelegate: NSObject, MGLMapViewDelegate {
}
}
}
- #else
- func mapView(mapView: MGLMapView, didSelectAnnotation annotation: MGLAnnotation) {
- let pointAnnotation = annotation as! MGLPointAnnotation
- let annotationView: PlaygroundAnnotationView = annotationViewByAnnotation[pointAnnotation]!
-
- for view in annotationViewByAnnotation.values {
- view.layer.zPosition = -1
- }
-
- annotationView.layer.zPosition = 1
-
- UIView.animateWithDuration(1.25, delay: 0, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.6, options: .CurveEaseOut, animations: {
- annotationView.transform = CGAffineTransformMakeScale(1.8, 1.8)
- }) { _ in
- annotationView.transform = CGAffineTransformMakeScale(1, 1)
-
- if deleteMarkerSwitchView.on {
- mapView.removeAnnotation(pointAnnotation)
- return
- }
-
- if hideMarkerSwitchView.on {
- annotationView.hidden = true
- }
- }
- }
- #endif
- func handleTap(press: UILongPressGestureRecognizer) {
+ @objc func handleTap(press: UILongPressGestureRecognizer) {
let mapView: MGLMapView = press.view as! MGLMapView
- #if swift(>=3)
- let isRecognized = press.state == .recognized
- #else
- let isRecognized = press.state == .Recognized
- #endif
+ let isRecognized = press.state == .recognized
+
if (isRecognized) {
- #if swift(>=3)
- let coordinate: CLLocationCoordinate2D = mapView.convert(press.location(in: mapView), toCoordinateFrom: mapView)
- #else
- let coordinate: CLLocationCoordinate2D = mapView.convertPoint(press.locationInView(mapView), toCoordinateFromView: mapView)
- #endif
+ let coordinate: CLLocationCoordinate2D = mapView.convert(press.location(in: mapView), toCoordinateFrom: mapView)
let annotation = MGLPointAnnotation()
annotation.title = "Dropped Marker"
annotation.coordinate = coordinate
@@ -218,11 +139,7 @@ let centerCoordinate = CLLocationCoordinate2D(latitude: 37.174057, longitude: -1
let mapView = MGLMapView(frame: CGRect(x: 0, y: 0, width: width, height: height))
mapView.frame = CGRect(x: 0, y: 0, width: width, height: height)
-#if swift(>=3)
- PlaygroundPage.current.liveView = mapView
-#else
- XCPlaygroundPage.currentPage.liveView = mapView
-#endif
+PlaygroundPage.current.liveView = mapView
let mapDelegate = MapDelegate()
mapView.delegate = mapDelegate
@@ -233,11 +150,7 @@ mapView.addGestureRecognizer(tapGesture)
//: Zoom in to a location
-#if swift(>=3)
- mapView.setCenter(centerCoordinate, zoomLevel: 12, animated: false)
-#else
- mapView.setCenterCoordinate(centerCoordinate, zoomLevel: 12, animated: false)
-#endif
+mapView.setCenter(centerCoordinate, zoomLevel: 12, animated: false)
//: Add control panel
diff --git a/platform/ios/Mapbox.playground/timeline.xctimeline b/platform/ios/Mapbox.playground/timeline.xctimeline
deleted file mode 100644
index bf468afeca..0000000000
--- a/platform/ios/Mapbox.playground/timeline.xctimeline
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Timeline
- version = "3.0">
- <TimelineItems>
- </TimelineItems>
-</Timeline>
diff --git a/platform/ios/README.md b/platform/ios/README.md
index 40e27c2d3f..86474e1ea9 100644
--- a/platform/ios/README.md
+++ b/platform/ios/README.md
@@ -2,11 +2,12 @@
[![Bitrise](https://www.bitrise.io/app/7514e4cf3da2cc57.svg?token=OwqZE5rSBR9MVWNr_lf4sA&branch=master)](https://www.bitrise.io/app/7514e4cf3da2cc57)
-A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder.
+A library based on [Mapbox GL Native](../../README.md) for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS using Objective-C, Swift, or Interface Builder.
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/MBXCustomCalloutView.m b/platform/ios/app/MBXCustomCalloutView.m
index 13564c5cbf..2d70e8b7b3 100644
--- a/platform/ios/app/MBXCustomCalloutView.m
+++ b/platform/ios/app/MBXCustomCalloutView.m
@@ -37,10 +37,9 @@ static CGFloat const tipWidth = 10.0;
return self;
}
-
#pragma mark - API
-- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated
+- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(__unused CGRect)constrainedRect animated:(BOOL)animated
{
if ([self.delegate respondsToSelector:@selector(calloutViewWillAppear:)])
{
@@ -108,5 +107,4 @@ static CGFloat const tipWidth = 10.0;
CGPathRelease(tipPath);
}
-
@end
diff --git a/platform/ios/app/MBXOfflinePacksTableViewController.m b/platform/ios/app/MBXOfflinePacksTableViewController.m
index 26a15a0b95..959ae57548 100644
--- a/platform/ios/app/MBXOfflinePacksTableViewController.m
+++ b/platform/ios/app/MBXOfflinePacksTableViewController.m
@@ -45,7 +45,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NS_DICTIONARY_OF(NSString *, id) *)change context:(void *)context {
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *, id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"packs"]) {
NSKeyValueChange changeKind = [change[NSKeyValueChangeKindKey] unsignedIntegerValue];
NSIndexSet *indices = change[NSKeyValueChangeIndexesKey];
@@ -119,9 +119,7 @@ static NSString * const MBXOfflinePacksTableViewActiveCellReuseIdentifier = @"Ac
}];
}];
[alertController addAction:downloadAction];
- if ([alertController respondsToSelector:@selector(setPreferredAction:)]) {
- alertController.preferredAction = downloadAction;
- }
+ alertController.preferredAction = downloadAction;
[self presentViewController:alertController animated:YES completion:nil];
}
diff --git a/platform/ios/app/MBXViewController.m b/platform/ios/app/MBXViewController.m
index 63b06af583..80163c7d40 100644
--- a/platform/ios/app/MBXViewController.m
+++ b/platform/ios/app/MBXViewController.m
@@ -54,6 +54,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsAnnotationsRows) {
MBXSettingsAnnotationsQueryAnnotations,
MBXSettingsAnnotationsCustomUserDot,
MBXSettingsAnnotationsRemoveAnnotations,
+ MBXSettingsAnnotationSelectRandomOffscreenPointAnnotation,
+ MBXSettingsAnnotationCenterSelectedAnnotation,
+ MBXSettingsAnnotationAddVisibleAreaPolyline
};
typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
@@ -75,11 +78,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsRuntimeStylingRows) {
MBXSettingsRuntimeStylingUpdateShapeSourceData,
MBXSettingsRuntimeStylingUpdateShapeSourceURL,
MBXSettingsRuntimeStylingUpdateShapeSourceFeatures,
- MBXSettingsRuntimeStylingVectorSource,
- MBXSettingsRuntimeStylingRasterSource,
+ MBXSettingsRuntimeStylingVectorTileSource,
+ MBXSettingsRuntimeStylingRasterTileSource,
MBXSettingsRuntimeStylingImageSource,
MBXSettingsRuntimeStylingRouteLine,
MBXSettingsRuntimeStylingDDSPolygon,
+ MBXSettingsRuntimeStylingCustomLatLonGrid,
};
typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@@ -88,7 +92,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
MBXSettingsMiscellaneousShowZoomLevel,
MBXSettingsMiscellaneousScrollView,
MBXSettingsMiscellaneousToggleTwoMaps,
- MBXSettingsMiscellaneousCountryLabels,
+ MBXSettingsMiscellaneousLocalizeLabels,
MBXSettingsMiscellaneousShowSnapshots,
MBXSettingsMiscellaneousShouldLimitCameraChanges,
MBXSettingsMiscellaneousPrintLogFile,
@@ -117,7 +121,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@interface MBXViewController () <UITableViewDelegate,
UITableViewDataSource,
- MGLMapViewDelegate>
+ MGLMapViewDelegate,
+ MGLComputedShapeSourceDataSource>
@property (nonatomic) IBOutlet MGLMapView *mapView;
@@ -125,7 +130,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@property (nonatomic) NSInteger styleIndex;
@property (nonatomic) BOOL debugLoggingEnabled;
@property (nonatomic) BOOL customUserLocationAnnnotationEnabled;
-@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
+@property (nonatomic, getter=isLocalizingLabels) BOOL localizingLabels;
@property (nonatomic) BOOL reuseQueueStatsEnabled;
@property (nonatomic) BOOL showZoomLevelEnabled;
@property (nonatomic) BOOL shouldLimitCameraChanges;
@@ -134,7 +139,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@interface MGLMapView (MBXViewController)
-@property (nonatomic) BOOL usingLocaleBasedCountryLabels;
@property (nonatomic) NSDictionary *annotationViewReuseQueueByIdentifier;
@end
@@ -169,12 +173,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self restoreState:nil];
self.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
- self.mapView.scaleBar.hidden = NO;
+ self.mapView.showsScale = YES;
self.mapView.showsUserHeadingIndicator = YES;
- self.hudLabel.hidden = YES;
- if ([UIFont respondsToSelector:@selector(monospacedDigitSystemFontOfSize:weight:)]) {
- self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
- }
+ self.hudLabel.titleLabel.font = [UIFont monospacedDigitSystemFontOfSize:10 weight:UIFontWeightRegular];
if ([MGLAccountManager accessToken].length)
{
@@ -204,11 +205,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView reloadStyle:self];
}];
[alertController addAction:OKAction];
+ alertController.preferredAction = OKAction;
- if ([alertController respondsToSelector:@selector(setPreferredAction:)])
- {
- alertController.preferredAction = OKAction;
- }
[self presentViewController:alertController animated:YES completion:nil];
}
}
@@ -221,6 +219,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[defaults setInteger:self.mapView.userTrackingMode forKey:@"MBXUserTrackingMode"];
[defaults setBool:self.mapView.showsUserLocation forKey:@"MBXShowsUserLocation"];
[defaults setInteger:self.mapView.debugMask forKey:@"MBXDebugMask"];
+ [defaults setBool:self.showZoomLevelEnabled forKey:@"MBXShowsZoomLevelHUD"];
[defaults synchronize];
}
@@ -246,6 +245,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
{
self.mapView.debugMask = (MGLMapDebugMaskOptions)uncheckedDebugMask;
}
+ if ([defaults boolForKey:@"MBXShowsZoomLevelHUD"])
+ {
+ self.showZoomLevelEnabled = YES;
+ [self updateHUD];
+ }
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations
@@ -333,6 +337,9 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Query Annotations",
[NSString stringWithFormat:@"%@ Custom User Dot", (_customUserLocationAnnnotationEnabled ? @"Disable" : @"Enable")],
@"Remove Annotations",
+ @"Select an offscreen point annotation",
+ @"Center selected annotation",
+ @"Add visible area polyline"
]];
break;
case MBXSettingsRuntimeStyling:
@@ -355,11 +362,12 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Update Shape Source: Data",
@"Update Shape Source: URL",
@"Update Shape Source: Features",
- @"Style Vector Source",
- @"Style Raster Source",
+ @"Style Vector Tile Source",
+ @"Style Raster Tile Source",
@"Style Image Source",
@"Add Route Line",
@"Dynamically Style Polygon",
+ @"Add Custom Lat/Lon Grid",
]];
break;
case MBXSettingsMiscellaneous:
@@ -369,7 +377,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[NSString stringWithFormat:@"%@ Zoom/Pitch/Direction Label", (_showZoomLevelEnabled ? @"Hide" :@"Show")],
@"Embedded Map View",
[NSString stringWithFormat:@"%@ Second Map", ([self.view viewWithTag:2] == nil ? @"Show" : @"Hide")],
- [NSString stringWithFormat:@"Show Labels in %@", (_usingLocaleBasedCountryLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
+ [NSString stringWithFormat:@"Show Labels in %@", (_localizingLabels ? @"Default Language" : [[NSLocale currentLocale] displayNameForKey:NSLocaleIdentifier value:[self bestLanguageForUser]])],
@"Show Snapshots",
[NSString stringWithFormat:@"%@ Camera Changes", (_shouldLimitCameraChanges ? @"Unlimit" : @"Limit")],
]];
@@ -460,6 +468,18 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsAnnotationsRemoveAnnotations:
[self.mapView removeAnnotations:self.mapView.annotations];
break;
+ case MBXSettingsAnnotationSelectRandomOffscreenPointAnnotation:
+ [self selectAnOffscreenPointAnnotation];
+ break;
+
+ case MBXSettingsAnnotationCenterSelectedAnnotation:
+ [self centerSelectedAnnotation];
+ break;
+
+ case MBXSettingsAnnotationAddVisibleAreaPolyline:
+ [self addVisibleAreaPolyline];
+ break;
+
default:
NSAssert(NO, @"All annotations setting rows should be implemented");
break;
@@ -522,11 +542,11 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsRuntimeStylingUpdateShapeSourceFeatures:
[self updateShapeSourceFeatures];
break;
- case MBXSettingsRuntimeStylingVectorSource:
- [self styleVectorSource];
+ case MBXSettingsRuntimeStylingVectorTileSource:
+ [self styleVectorTileSource];
break;
- case MBXSettingsRuntimeStylingRasterSource:
- [self styleRasterSource];
+ case MBXSettingsRuntimeStylingRasterTileSource:
+ [self styleRasterTileSource];
break;
case MBXSettingsRuntimeStylingImageSource:
[self styleImageSource];
@@ -537,6 +557,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;
@@ -545,7 +568,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
case MBXSettingsMiscellaneous:
switch (indexPath.row)
{
- case MBXSettingsMiscellaneousCountryLabels:
+ case MBXSettingsMiscellaneousLocalizeLabels:
[self styleCountryLabelsLanguage];
break;
case MBXSettingsMiscellaneousWorldTour:
@@ -907,8 +930,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"];
@@ -916,10 +939,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"];
@@ -934,48 +957,47 @@ 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]};
+ NSExpression *fillColorExpression = [NSExpression mgl_expressionForInterpolatingExpression:NSExpression.zoomLevelVariableExpression
+ withCurveType:MGLExpressionInterpolationModeLinear
+ parameters:nil
+ stops:[NSExpression expressionForConstantValue:waterColorStops]];
+ waterLayer.fillColor = fillColorExpression;
+
+ NSDictionary *fillAntialiasedStops = @{@11: @YES,
+ @12: @NO,
+ @13: @YES,
+ @14: @NO,
+ @15: @YES};
+ waterLayer.fillAntialiased = [NSExpression mgl_expressionForSteppingExpression:NSExpression.zoomLevelVariableExpression
+ fromExpression:[NSExpression expressionForConstantValue:@NO]
+ stops:[NSExpression expressionForConstantValue: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:
+ @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
+ lineWidthStops];
+ roadLayer.lineWidth = lineWidthExpression;
+ roadLayer.lineGapWidth = lineWidthExpression;
+
+ NSDictionary *roadLineColorStops = @{@10: [UIColor purpleColor],
+ @13: [UIColor yellowColor],
+ @16: [UIColor cyanColor]};
+ roadLayer.lineColor = [NSExpression expressionWithFormat:
+ @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
+ roadLineColorStops];
roadLayer.visible = YES;
roadLayer.maximumZoomLevel = 15;
@@ -985,15 +1007,15 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)styleRasterLayer
{
NSURL *rasterURL = [NSURL URLWithString:@"mapbox://mapbox.satellite"];
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"my-raster-source" configurationURL:rasterURL tileSize:512];
- [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;
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"my-raster-tile-source" configurationURL:rasterURL tileSize:512];
+ [self.mapView.style addSource:rasterTileSource];
+
+ MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"my-raster-layer" source:rasterTileSource];
+ NSDictionary *opacityStops = @{@20.0f: @1.0f,
+ @5.0f: @0.0f};
+ rasterLayer.rasterOpacity = [NSExpression expressionWithFormat:
+ @"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
+ opacityStops];
[self.mapView.style addLayer:rasterLayer];
}
@@ -1005,7 +1027,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];
}
@@ -1013,7 +1035,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
@@ -1021,13 +1043,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
@@ -1051,8 +1073,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];
});
}
@@ -1071,9 +1093,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];
});
}
@@ -1092,8 +1114,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];
});
}
@@ -1123,8 +1145,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];
});
}
@@ -1177,8 +1199,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 ] }}";
@@ -1189,7 +1210,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];
@@ -1206,7 +1227,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];
}
@@ -1283,8 +1304,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(), ^{
@@ -1310,36 +1330,34 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self.mapView.style addLayer:layer];
}
-- (void)styleVectorSource
+- (void)styleVectorTileSource
{
NSURL *url = [[NSURL alloc] initWithString:@"mapbox://mapbox.mapbox-terrain-v2"];
- MGLVectorSource *vectorSource = [[MGLVectorSource alloc] initWithIdentifier:@"style-vector-source-id" configurationURL:url];
- [self.mapView.style addSource:vectorSource];
+ MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"style-vector-tile-source-id" configurationURL:url];
+ [self.mapView.style addSource:vectorTileSource];
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];
+ MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"style-vector-line-layer-id" source:vectorTileSource];
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];
}
-- (void)styleRasterSource
+- (void)styleRasterTileSource
{
NSString *tileURL = [NSString stringWithFormat:@"https://stamen-tiles.a.ssl.fastly.net/terrain-background/{z}/{x}/{y}%@.jpg", UIScreen.mainScreen.nativeScale > 1 ? @"@2x" : @""];
- MGLRasterSource *rasterSource = [[MGLRasterSource alloc] initWithIdentifier:@"style-raster-source-id" tileURLTemplates:@[tileURL] options:@{
+ MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"style-raster-tile-source-id" tileURLTemplates:@[tileURL] options:@{
MGLTileSourceOptionTileSize: @256,
}];
- [self.mapView.style addSource:rasterSource];
+ [self.mapView.style addSource:rasterTileSource];
- MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"style-raster-layer-id" source:rasterSource];
+ MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"style-raster-layer-id" source:rasterTileSource];
[self.mapView.style addLayer:rasterLayer];
}
@@ -1378,8 +1396,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
-(void)styleCountryLabelsLanguage
{
- _usingLocaleBasedCountryLabels = !_usingLocaleBasedCountryLabels;
- self.mapView.style.localizesLabels = _usingLocaleBasedCountryLabels;
+ _localizingLabels = !_localizingLabels;
+ [self.mapView.style localizeLabelsIntoLocale:_localizingLabels ? [NSLocale localeWithLocaleIdentifier:@"mul"] : nil];
}
- (void)styleRouteLine
@@ -1404,19 +1422,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];
}
@@ -1446,22 +1464,34 @@ 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 mgl_expressionForConditional:[NSPredicate predicateWithFormat:@"fill == YES"]
+ trueExpression:[NSExpression expressionForConstantValue:[UIColor greenColor]]
+ falseExpresssion:[NSExpression expressionForConstantValue:[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 mgl_expressionForConditional:[NSPredicate predicateWithFormat:@"opacity != nil"]
+ trueExpression:[NSExpression expressionForKeyPath:@"opacity"]
+ falseExpresssion:[NSExpression expressionForConstantValue:@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
@@ -1497,7 +1527,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self continueWorldTourWithRemainingAnnotations:annotations];
}
-- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations
+- (void)continueWorldTourWithRemainingAnnotations:(NSMutableArray<MGLPointAnnotation *> *)annotations
{
MGLPointAnnotation *nextAnnotation = annotations.firstObject;
if (!nextAnnotation || !_isTouringWorld)
@@ -1541,6 +1571,73 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[self presentViewController:alertController animated:YES completion:nil];
}
+- (id<MGLAnnotation>)randomOffscreenPointAnnotation {
+
+ NSPredicate *pointAnnotationPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [evaluatedObject isKindOfClass:[MGLPointAnnotation class]];
+ }];
+
+ NSArray *annotations = [self.mapView.annotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (annotations.count == 0) {
+ return nil;
+ }
+
+ NSArray *visibleAnnotations = [self.mapView.visibleAnnotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (visibleAnnotations.count == annotations.count) {
+ return nil;
+ }
+
+ NSMutableArray *invisibleAnnotations = [annotations mutableCopy];
+
+ if (visibleAnnotations.count > 0) {
+ [invisibleAnnotations removeObjectsInArray:visibleAnnotations];
+ }
+
+ // Now pick a random offscreen annotation.
+ uint32_t index = arc4random_uniform((uint32_t)invisibleAnnotations.count);
+ return invisibleAnnotations[index];
+}
+
+- (void)selectAnOffscreenPointAnnotation {
+ id<MGLAnnotation> annotation = [self randomOffscreenPointAnnotation];
+ if (annotation) {
+ [self.mapView selectAnnotation:annotation animated:YES];
+
+ NSAssert(self.mapView.selectedAnnotations.firstObject, @"The annotation was not selected");
+ }
+}
+
+- (void)centerSelectedAnnotation {
+ id<MGLAnnotation> annotation = self.mapView.selectedAnnotations.firstObject;
+
+ if (!annotation)
+ return;
+
+ CGPoint point = [self.mapView convertCoordinate:annotation.coordinate toPointToView:self.mapView];
+
+ // Animate, so that point becomes the the center
+ CLLocationCoordinate2D center = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
+ [self.mapView setCenterCoordinate:center animated:YES];
+}
+
+- (void)addVisibleAreaPolyline {
+ CGRect constrainedRect = UIEdgeInsetsInsetRect(self.mapView.bounds, self.mapView.contentInset);
+
+ CLLocationCoordinate2D lineCoords[5];
+
+ lineCoords[0] = [self.mapView convertPoint: CGPointMake(CGRectGetMinX(constrainedRect), CGRectGetMinY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[1] = [self.mapView convertPoint: CGPointMake(CGRectGetMaxX(constrainedRect), CGRectGetMinY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[2] = [self.mapView convertPoint: CGPointMake(CGRectGetMaxX(constrainedRect), CGRectGetMaxY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[3] = [self.mapView convertPoint: CGPointMake(CGRectGetMinX(constrainedRect), CGRectGetMaxY(constrainedRect)) toCoordinateFromView:self.mapView];
+ lineCoords[4] = lineCoords[0];
+
+ MGLPolyline *line = [MGLPolyline polylineWithCoordinates:lineCoords
+ count:sizeof(lineCoords)/sizeof(lineCoords[0])];
+ [self.mapView addAnnotation:line];
+}
+
- (void)printTelemetryLogFile
{
NSString *fileContents = [NSString stringWithContentsOfFile:[self telemetryDebugLogFilePath] encoding:NSUTF8StringEncoding error:nil];
@@ -1592,7 +1689,13 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
toCoordinateFromView:self.mapView];
pin.title = title ?: @"Dropped Pin";
pin.subtitle = [[[MGLCoordinateFormatter alloc] init] stringFromCoordinate:pin.coordinate];
- // Calling `addAnnotation:` on mapView is not required since `selectAnnotation:animated` has the side effect of adding the annotation if required
+
+
+ // Calling `addAnnotation:` on mapView is required here (since `selectAnnotation:animated` has
+ // the side effect of adding the annotation if required, but returning an incorrect callout
+ // positioning rect)
+
+ [self.mapView addAnnotation:pin];
[self.mapView selectAnnotation:pin animated:YES];
}
}
@@ -1611,8 +1714,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
@"Dark",
@"Satellite",
@"Satellite Streets",
- @"Traffic Day",
- @"Traffic Night",
];
styleURLs = @[
[MGLStyle streetsStyleURL],
@@ -1620,9 +1721,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
[MGLStyle lightStyleURL],
[MGLStyle darkStyleURL],
[MGLStyle satelliteStyleURL],
- [MGLStyle satelliteStreetsStyleURL],
- [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"],
- [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"],
+ [MGLStyle satelliteStreetsStyleURL]
];
NSAssert(styleNames.count == styleURLs.count, @"Style names and URLs don’t match.");
@@ -1713,12 +1812,6 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
// Comment out the pin dropping functionality in the handleLongPress:
// method in this class to make draggable annotation views play nice.
annotationView.draggable = YES;
-
- // Uncomment to force annotation view to maintain a constant size when
- // the map is tilted. By default, annotation views will shrink and grow
- // as they move towards and away from the horizon. Relatedly, annotations
- // backed by GL sprites currently ONLY scale with viewing distance.
- // annotationView.scalesWithViewingDistance = NO;
} else {
// orange indicates that the annotation view was reused
annotationView.backgroundColor = [UIColor orangeColor];
@@ -1893,7 +1986,7 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
// Default Mapbox styles use {name_en} as their label language, which means
// that a device with an English-language locale is already effectively
// using locale-based country labels.
- _usingLocaleBasedCountryLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
+ _localizingLabels = [[self bestLanguageForUser] isEqualToString:@"en"];
}
- (BOOL)mapView:(MGLMapView *)mapView shouldChangeFromCamera:(MGLMapCamera *)oldCamera toCamera:(MGLMapCamera *)newCamera {
@@ -1938,6 +2031,8 @@ typedef NS_ENUM(NSInteger, MBXSettingsMiscellaneousRows) {
- (void)updateHUD {
if (!self.reuseQueueStatsEnabled && !self.showZoomLevelEnabled) return;
+ if (self.hudLabel.hidden) self.hudLabel.hidden = NO;
+
NSString *hudString;
if (self.reuseQueueStatsEnabled) {
@@ -1953,4 +2048,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/Main.storyboard b/platform/ios/app/Main.storyboard
index 507582213f..04e4f9ab45 100644
--- a/platform/ios/app/Main.storyboard
+++ b/platform/ios/app/Main.storyboard
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G8c" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="PSe-Ot-7Ff">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
- <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="Navigation items with more than one left or right bar item" minToolsVersion="7.0"/>
@@ -26,40 +26,44 @@
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kNe-zV-9ha" customClass="MGLMapView">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+ <subviews>
+ <button hidden="YES" opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
+ <rect key="frame" x="8" y="102" width="40" height="20"/>
+ <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
+ <accessibility key="accessibilityConfiguration">
+ <accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
+ </accessibility>
+ <constraints>
+ <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="viz-kn-dfK"/>
+ <constraint firstAttribute="height" constant="20" id="zSU-Mb-f1v"/>
+ </constraints>
+ <fontDescription key="fontDescription" type="system" pointSize="10"/>
+ <inset key="contentEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
+ <userDefinedRuntimeAttributes>
+ <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
+ <integer key="value" value="2"/>
+ </userDefinedRuntimeAttribute>
+ </userDefinedRuntimeAttributes>
+ </button>
+ </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<gestureRecognizers/>
+ <constraints>
+ <constraint firstItem="58y-pX-YyB" firstAttribute="top" secondItem="kNe-zV-9ha" secondAttribute="topMargin" constant="30" id="89S-qk-mPR"/>
+ <constraint firstItem="58y-pX-YyB" firstAttribute="leading" secondItem="kNe-zV-9ha" secondAttribute="leadingMargin" id="cXU-Qh-ilW"/>
+ <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="58y-pX-YyB" secondAttribute="trailing" id="txU-Gp-2du"/>
+ </constraints>
<connections>
<outlet property="delegate" destination="WaX-pd-UZQ" id="za0-3B-qR6"/>
<outletCollection property="gestureRecognizers" destination="lfd-mn-7en" appends="YES" id="0PH-gH-GRm"/>
</connections>
</view>
- <button opaque="NO" userInteractionEnabled="NO" alpha="0.69999999999999996" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="58y-pX-YyB">
- <rect key="frame" x="319" y="606" width="40" height="21"/>
- <color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
- <accessibility key="accessibilityConfiguration">
- <accessibilityTraits key="traits" button="YES" notEnabled="YES"/>
- </accessibility>
- <constraints>
- <constraint firstAttribute="width" relation="greaterThanOrEqual" constant="40" id="OL2-l5-I2f"/>
- <constraint firstAttribute="height" relation="greaterThanOrEqual" constant="20" id="xHg-ye-wzT"/>
- </constraints>
- <fontDescription key="fontDescription" type="system" pointSize="10"/>
- <inset key="contentEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
- <userDefinedRuntimeAttributes>
- <userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
- <integer key="value" value="2"/>
- </userDefinedRuntimeAttribute>
- </userDefinedRuntimeAttributes>
- </button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="kNe-zV-9ha" firstAttribute="leading" secondItem="Z9X-fc-PUC" secondAttribute="leading" id="53e-Tz-QxF"/>
<constraint firstItem="kNe-zV-9ha" firstAttribute="bottom" secondItem="m8o-i7-QIy" secondAttribute="top" id="Etp-BC-E1N"/>
<constraint firstAttribute="trailing" secondItem="kNe-zV-9ha" secondAttribute="trailing" id="MGr-8G-VEb"/>
- <constraint firstItem="58y-pX-YyB" firstAttribute="trailing" secondItem="Z9X-fc-PUC" secondAttribute="trailingMargin" id="O3a-bR-boI"/>
- <constraint firstItem="58y-pX-YyB" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Z9X-fc-PUC" secondAttribute="leadingMargin" id="ceH-yz-ewY"/>
- <constraint firstItem="m8o-i7-QIy" firstAttribute="top" secondItem="58y-pX-YyB" secondAttribute="bottom" constant="40" id="cjh-ZS-Mv4"/>
<constraint firstItem="kNe-zV-9ha" firstAttribute="top" secondItem="Z9X-fc-PUC" secondAttribute="top" id="qMm-e9-jxH"/>
</constraints>
</view>
@@ -133,14 +137,14 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Inactive Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JtH-Ce-MI5">
- <rect key="frame" x="15" y="6" width="174.5" height="19.5"/>
+ <rect key="frame" x="16" y="6" width="174.5" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="456 resources (789 MB)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tTJ-jv-U9v">
- <rect key="frame" x="15" y="25.5" width="128" height="13.5"/>
+ <rect key="frame" x="16" y="25.5" width="128.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -157,14 +161,14 @@
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="My Active Offline Pack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="9ZK-gS-wJ4">
- <rect key="frame" x="15" y="6" width="163" height="19.5"/>
+ <rect key="frame" x="16" y="6" width="163" height="19.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Downloading 123 of 456 resources… (789 MB downloaded)" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0xK-p8-Mmh">
- <rect key="frame" x="15" y="25.5" width="310.5" height="13.5"/>
+ <rect key="frame" x="16" y="25.5" width="311" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -201,7 +205,7 @@
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="PSe-Ot-7Ff" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="ONr-CS-J5X">
- <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+ <rect key="frame" x="0.0" y="20" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
diff --git a/platform/ios/app/da.lproj/Localizable.strings b/platform/ios/app/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/ios/app/da.lproj/Localizable.strings
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/app/pt-PT.lproj/Localizable.strings b/platform/ios/app/pt-PT.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/ios/app/pt-PT.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/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm
index d4629e2521..84c2790d50 100644
--- a/platform/ios/benchmark/MBXBenchViewController.mm
+++ b/platform/ios/benchmark/MBXBenchViewController.mm
@@ -76,10 +76,8 @@
[self startBenchmarkIteration];
}];
[alertController addAction:OKAction];
+ alertController.preferredAction = OKAction;
- if ([alertController respondsToSelector:@selector(setPreferredAction:)]) {
- alertController.preferredAction = OKAction;
- }
[self presentViewController:alertController animated:YES completion:nil];
}
}
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/doc-README.md b/platform/ios/docs/doc-README.md
index 7cd0376d07..3a95aa96de 100644
--- a/platform/ios/docs/doc-README.md
+++ b/platform/ios/docs/doc-README.md
@@ -1,6 +1,6 @@
# [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)
-The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
![Mapbox Maps SDK for iOS screenshots](img/screenshot.png)
diff --git a/platform/ios/docs/guides/Adding Markers to a Map.md b/platform/ios/docs/guides/Adding Markers to a Map.md
new file mode 100644
index 0000000000..b33b536d44
--- /dev/null
+++ b/platform/ios/docs/guides/Adding Markers to a Map.md
@@ -0,0 +1,62 @@
+# Adding Markers to a Map
+
+Mapbox offers a few different ways to add markers to a map, each with different tradeoffs. Below is an overview of the variety of approaches that can be used.
+
+## **Annotations API**
+
+Our annotations API includes the `MGLAnnotationImage`, and `MGLAnnotationView` classes. These are most similar to MapKit’s annotation class and provide a familiar interface for working with markers and callouts.
+
+| <img src="img/adding-points-to-a-map/annotation-image.png" alt="MGLAnnotationImage" style="height: 500px;"/> | <img src="img/adding-points-to-a-map/annotation-view.png" alt="MGLAnnotationView" style="height: 500px;"/> |
+|----------------------|---------------------|
+| `MGLAnnotationImage` | `MGLAnnotationView` |
+
+**MGLAnnotationImage** is an annotation class that is easily customizable with any `UIImage`.
+It is highly performant, as the images are rendered directly using OpenGL. However, if you need to animate your annotations or control z-layer ordering, consider working with **MGLAnnotationView** which supports any animation that can be applied to a `UIView`. View hierarchy can be manipulated by using instance methods available on `UIView` such as `-[UIView bringSubviewToFront:]`.
+
+**MGLAnnotationView** is an annotation class that is an easily customizable `UIView`. Use this class if you need your markers to be dynamic or animated. `MGLAnnotationView` has a significant advantage over `MGLAnnotationImage` when you need every annotation to be unique. For example, annotation views are ideal for showing user locations on a map using high-resolution profile pictures. However, the map can slow down when many annotation views are visible at the same time, so if you need to add a very large number of markers, consider using our runtime styling APIs instead.
+
+Both `MGLAnnotationImage` and `MGLAnnotationView` can become interactive by adding a [few lines of code](https://www.mapbox.com/ios-sdk/examples/marker/). When the user taps an annotation, the annotation’s name appears in a basic callout. An annotation view can additionally respond to [drag-and-drop gestures](https://www.mapbox.com/ios-sdk/examples/draggable-views/).
+
+## **Runtime styling API**
+
+For full control of how markers are displayed on a map, consider using our [runtime styling](runtime-styling.html) APIs. Like `MGLAnnotationImage`, it is a performant approach to adding markers because they rendered directly using OpenGL. However, the runtime styling APIs also provide support for rendering labels together with icons, finer control of z-ordering, and clustering, so consider using this set of APIs if you need to display a large amount of highly customizable markers.
+
+Our runtime styling API is the most powerful option if you need to create rich data visualizations within in your map, but it is the most complex and has a steeper learning curve than our annotations API.
+
+The runtime styling API includes our `MGLSymbolStyleLayer` and `MGLCircleStyleLayer` classes that can be used to dynamically display on markers on map when used in conjunction with either an `MGLVectorSource` or an `MGLShapeSource`.
+
+If you need to implement callouts with the `MGLSymbolStyleLayer` or `MGLCircleStyleLayer`, you will need to implement your own tap gesture recognizer that calls `-[MGLMapView visibleFeaturesAtPoint:inStyleLayersWithIdentifiers:]` to get the tapped point feature, then show a `UIView` you provide. Additionally, if you need to animate markers when using the runtime styling APIs, consider using a timer to update the source data coordinates accordingly.
+
+| <img src="img/adding-points-to-a-map/circle-layer.png" alt="MGLCircleStyleLayer" style="height: 500px;"/> | <img src="img/adding-points-to-a-map/symbol-layer.png" alt="MGLSymbolStyleLayer" style="height: 500px;"/> |
+|----------------------|---------------------|
+| `MGLCircleStyleLayer` | `MGLSymbolStyleLayer` |
+
+The **MGLCircleStyleLayer** class is the style layer class responsible for displaying the source’s point features as circle-shaped markers. You can specify circle fill and outline colors, as well as size. You can also dynamically change the circle’s styling properties based on any attributes your source data contains.
+
+The **MGLSymbolStyleLayer** class is the style layer class responsible for displaying the source’s point features as icons and labels. You can use custom images as icons and also combine text labels, placing them exactly where you specify. You can also dynamically change the symbol’s styling properties based on any attributes your source data contains.
+
+Still undecided on which approach will work best for your use case? [Reach out to our support team](https://www.mapbox.com/contact/).
+
+See the table below for a summary of APIs that can be used to add markers to a map:
+
+✅ Recommended
+
+⚠️ Supported with caveats
+
+➖ Unavailable or not supported
+
+
+| Feature | MGLAnnotationView | MGLAnnotationImage | MGLSymbolStyleLayer | MGLCircleStyleLayer |
+|----------------------------------------------------|--------------------------------------------------------------------|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
+| Customizability | ✅ Text labels, interactive subviews | ⚠️ Static images only | ✅ Full support for text labels and label placement | ✅Customize circle color and outline |
+| Borrows familiar concepts from | MapKit, Google Maps SDK | ➖ | Mapbox GL JS, Mapbox Studio | Mapbox GL JS, Mapbox Studio |
+| Can use images | ✅ | ✅ | ✅ | ➖ |
+| Can use text | ✅ | ➖ | ✅ | ➖ |
+| Control Z-index | ✅ | ➖ | ⚠️ Add multiple layers at, above, or below a specified layer index to control ordering | ⚠️ Add multiple layers at, above, or below a specified layer index to control ordering |
+| Drag and drop | ✅ | ➖ | ➖ | ➖ |
+| Core Animation support | ✅ | ➖ | ➖ | ➖ |
+| Add/move/replace data | ✅ | ✅ | ⚠️ Partial data updates are less performant than using annotations | ⚠️ Partial data updates are less performant than using annotations |
+| SceneKit support | ✅ | ➖ | ➖ | ➖ |
+| Can be dynamically styled based on data attributes | ✅ Subclass `MGLPointAnnotation` to add custom attributes | ✅ Subclass `MGLPointAnnotation` to add custom attributes | ✅ | ✅ |
+| Supports callouts | ✅ Built-in callouts included | ✅ Built-in callouts included | ⚠️ Implement your own gesture recognizer that uses feature querying, then create custom UIViews to mimic native callouts | ⚠️ Implement your own gesture recognizer that uses feature querying, then create custom UIViews to mimic native callouts |
+| Supports clustering | ⚠️ Use a [third-party plugin](https://github.com/hulab/ClusterKit/) | ➖ | ✅ | ✅ |
diff --git a/platform/ios/docs/guides/Adding Points to a Map.md b/platform/ios/docs/guides/Adding Points to a Map.md
deleted file mode 100644
index 2844075cc7..0000000000
--- a/platform/ios/docs/guides/Adding Points to a Map.md
+++ /dev/null
@@ -1,82 +0,0 @@
-# Adding Points to a Map
-
-Mapbox offers a few different ways to add points to a map, each with different tradeoffs.
-
-## MGLPointAnnotation
-
-It’s straightforward to add an annotation to a map. You can use `MGLPointAnnotation` as is, or you can subclass it to add annotations with richer data.
-
-```swift
-let annotation = MGLPointAnnotation()
-annotation.coordinate = CLLocationCoordinate2D(latitude: 45.5076, longitude: -122.6736)
-annotation.title = "Bobby's Coffee"
-annotation.subtitle = "Coffeeshop"
-mapView.addAnnotation(annotation)
-```
-
-See the `MGLMapViewDelegate` method `-mapView:annotationCanShowCallout:` and similar methods for allowing interaction with a callout ([example](https://www.mapbox.com/ios-sdk/examples/callout-delegate/)).
-
-## Displaying annotations
-
-There are two basic ways to display the annotations you’ve added to a map, each with their own tradeoffs.
-
-### Annotation Images (`MGLAnnotationImage`)
-
-Annotation images are the quickest and most performant way to display annotations, but are also the most basic.
-
-By default, annotations added to the map are displayed with a red pin ([example](https://www.mapbox.com/ios-sdk/examples/marker/)). To use custom images, you can implement `MGLMapViewDelegate` `-mapView:imageForAnnotation:` ([example](https://www.mapbox.com/ios-sdk/examples/marker-image/)).
-
-**Pros**
-
-* The easiest way to display a marker on a map
-* Easily customizable with any `UIImage`
-* High performance, as the images are rendered directly in OpenGL
-
-**Cons**
-
-* Annotation images are purely static and cannot be animated
-* No control over z-ordering
-
-### Annotation Views (`MGLAnnotationView`)
-
-If you’re looking to add custom `UIView`s or have annotations that are dynamic or animatable, consider an `MGLAnnotationView` instead of an `MGLAnnotationImage` ([example](https://www.mapbox.com/ios-sdk/examples/annotation-views/)).
-
-Annotation views have significant advantages over annotation images when you need every annotation to be unique. For example, annotation views are ideal for showing user locations on a map using high-resolution profile pictures.
-
-To use annotation views, implement `MGLMapViewDelegate` `-mapView:viewForAnnotation` and provide a custom `MGLAnnotationView` (`UIView`) subclass.
-
-**Pros**
-
-* Custom, native UIViews
-* No limit on style or image size
-* Full support for animations
-* Relative control over z-ordering using the `zPosition` property on `CALayer`
-* Familiar API for MapKit users
-
-**Cons**
-
-* Performance implications:
- * `UIView`s are inherently slow to render compared to OpenGL, more apparent if you’re adding many views or moving the map rapidly
- * In some cases, you might consider runtime styling
-
-## Advanced: Runtime Styling
-
-For absolute full control of how points are displayed on a map, consider [runtime styling](runtime-styling.html).
-
-You can use `MGLPointFeature` or any other [style primitives](Style%20Primitives.html) to add points and shapes to an `MGLShapeSource`.
-
-From there, you can create one or many `MGLSymbolStyleLayer` or `MGLCircleStyleLayer` layers to filter and style points for display on the map ([example](https://www.mapbox.com/ios-sdk/examples/runtime-multiple-annotations)).
-
-**Pros**
-
-* Most powerful option
-* Highest performance (rendered in GL directly)
-* SDK-level support for labels rendered together with icons
-* Finer control of z-ordering
- * Rendering respects ordering within the data source
- * Otherwise layers are lightweight so you can create a new layer for each level you need
-
-**Cons**
-
-* Currently you must implement your own tap gesture recognizer together with `MGLMapView.visibleFeaturesAtPoint` to recognize taps and manually show callouts ([example](https://www.mapbox.com/ios-sdk/examples/select-layer/)).
-* Currently no SDK support for animations. If you need animations, consider using an NSTimer and updating the layer properties accordingly.
diff --git a/platform/ios/docs/guides/For Style Authors.md b/platform/ios/docs/guides/For Style Authors.md
index ebf4eb23d6..b3beea8540 100644
--- a/platform/ios/docs/guides/For Style Authors.md
+++ b/platform/ios/docs/guides/For Style Authors.md
@@ -127,21 +127,22 @@ source object is a member of one of the following subclasses of `MGLSource`:
In style JSON | In the SDK
--------------|-----------
+`vector` | `MGLVectorTileSource`
+`raster` | `MGLRasterTileSource`
+`raster-dem` | `MGLRasterDEMSource`
`geojson` | `MGLShapeSource`
-`raster` | `MGLRasterSource`
-`vector` | `MGLVectorSource`
`image` | `MGLImageSource`
`canvas` and `video` sources are not supported.
### Tile sources
-Raster and vector sources may be defined in TileJSON configuration files. This
-SDK supports the properties defined in the style specification, which are a
+Raster and vector tile sources may be defined in TileJSON configuration files.
+This SDK supports the properties defined in the style specification, which are a
subset of the keys defined in version 2.1.0 of the
[TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
specification. As an alternative to authoring a custom TileJSON file, you may
-supply various tile source options when creating a raster or vector source.
+supply various tile source options when creating a raster or vector tile source.
These options are detailed in the `MGLTileSourceOption` documentation:
In style JSON | In TileJSON | In the SDK
@@ -154,6 +155,7 @@ In style JSON | In TileJSON | In the SDK
`tileSize` | — | `MGLTileSourceOptionTileSize`
`attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security)
`scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem`
+`encoding` | – | `MGLTileSourceOptionDEMEncoding`
### Shape sources
@@ -192,6 +194,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 +275,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 +295,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,42 +310,88 @@ 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
+
+Many expression operators defined in the style specification have corresponding
+symbols to be used with the `+[NSExpression expressionWithFormat:]`,
+`+[NSExpression expressionForFunction:arguments:]`, or
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method:
+
+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:` | `CAST(zipCode, 'NSNumber')`
+`to-string` | `stringValue` | `CAST(ele, 'NSString')`
+`typeof` | |
+`geometry-type` | `NSExpression.geometryTypeVariableExpression` | `$geometryType`
+`id` | `NSExpression.featureIdentifierVariableExpression` | `$featureIdentifier`
+`properties` | `NSExpression.featureAttributesVariableExpression` | `$featureAttributes`
+`at` | `objectFrom:withIndex:` | `array[n]`
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
+`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:]` or `MGL_IF` or `+[NSExpression mgl_expressionForConditional:trueExpression:falseExpresssion:]` | `TERNARY(1 = 2, YES, NO)` or `MGL_IF(1 = 2, YES, 2 = 2, YES, NO)`
+`coalesce` | `mgl_coalesce:` | `mgl_coalesce({x, y, z})`
+`match` | `MGL_MATCH` or `+[NSExpression mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')`
+`interpolate` | `mgl_interpolate:withCurveType:parameters:stops:` or `+[NSExpression mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` |
+`step` | `mgl_step:withMinimum:stops:` or `+[NSExpression mgl_expressionForSteppingExpression:fromExpression:stops:]` |
+`let` | `mgl_expressionWithContext:` | `MGL_LET('ios', 11, 'macos', 10.13, $ios + $macos)`
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `mgl_join:` or `-[NSExpression mgl_expressionByAppendingExpression:]` | `mgl_join({'Old', ' ', 'MacDonald'})`
+`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`
+`abs` | `abs:` | `abs(-1)`
+`acos` | `mgl_acos:` | `mgl_acos(1)`
+`asin` | `mgl_asin:` | `mgl_asin(0)`
+`atan` | `mgl_atan:` | `mgl_atan(20)`
+`ceil` | `ceiling:` | `ceiling(0.99999)`
+`cos` | `mgl_cos:` | `mgl_cos(0)`
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`floor` | `floor:` | `floor(-0.99999)`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | `mgl_log2:` | `mgl_log2(1024)`
+`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`
+`round` | `mgl_round:` | `mgl_round(1.5)`
+`sin` | `mgl_sin:` | `mgl_sin(0)`
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | `mgl_tan:` | `mgl_tan(0)`
+`zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoom`
+`heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity`
+
+For operators that have no corresponding `NSExpression` symbol, use the
+`MGL_FUNCTION()` format string syntax.
## Filtering sources
-You can filter a shape or vector source by setting the
+You can filter a shape or vector tile source by setting the
`MGLVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a
table of style JSON operators and the corresponding operators used in the
predicate format string:
@@ -358,5 +412,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/Info.plist Keys.md b/platform/ios/docs/guides/Info.plist Keys.md
index bc2f3f5786..cf00ec5499 100644
--- a/platform/ios/docs/guides/Info.plist Keys.md
+++ b/platform/ios/docs/guides/Info.plist Keys.md
@@ -8,7 +8,7 @@ Set the [Mapbox access token](https://www.mapbox.com/help/define-access-token/)
Mapbox-hosted vector tiles and styles require an API access token, which you can obtain from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). Access tokens associate requests to Mapbox’s vector tile and style APIs with your Mapbox account. They also deter other developers from using your styles without your permission.
-As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private.
+As an alternative, you can use `MGLAccountManager.accessToken` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private.
## MGLMapboxAPIBaseURL
diff --git a/platform/ios/docs/guides/Migrating to Expressions.md b/platform/ios/docs/guides/Migrating to Expressions.md
new file mode 100644
index 0000000000..96d4df6853
--- /dev/null
+++ b/platform/ios/docs/guides/Migrating to Expressions.md
@@ -0,0 +1,266 @@
+<!--
+ This file is generated.
+ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+-->
+
+# Migrating from Style Functions to Expressions
+
+[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
+
+With Mapbox Maps SDK for iOS v4.0.0, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions.
+
+An expression is represented at runtime by the `NSExpression` class. Expressions can be used to style paint and layout properties based on zoom level, data attributes, or a combination of the two.
+
+A constant expression can also be assigned to a style property. For example, the opacity of a fill style layer can be set to a constant value between 0 and 1.
+
+The documentation for each individual style layer property notes which non-constant expressions are enabled for that property. Style functions supported four interpolation modes: exponential, interval, categorical, and identity.
+
+This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php). Under each interpolation mode, the style function implementation will be shown, followed by the current syntax.
+
+For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. To learn more about supported expressions, see our ["Predicates and Expressions"](predicates-and-expressions.html) guide. The "Predicates and Expressions" guide also outlines Mapbox custom functions that can be used to dynamically style a map.
+
+## Stops
+Stops are dictionary keys that are associated with layer attribute values. Constant values no longer need to be wrapped as style values when they are values in a stops dictionary.
+
+
+Style function syntax:
+
+```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),
+]
+```
+
+Current syntax:
+```swift
+let stops: [NSNumber: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+```
+
+
+## Interpolation mode
+
+Style functions supported four interpolation modes: exponential/linear, interval, categorical, and identity. For more information about supported custom expressions, please see the "Predicates and Expressions" guide.
+
+### Linear
+
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` takes the interpolation type as a parameter. If you previously used the default interpolation base, use the curve type `MGLExpressionInterpolationMode.linear`. See the [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code) documentation for more details.
+
+The stops dictionary below, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
+
+Style function syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+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 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)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+Current syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil)
+mapView.style?.addSource(source)
+
+let stops: [NSNumber: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+
+let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
+ stops)
+layer.circleRadius = NSExpression(forConstantValue: 10)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+### Exponential
+
+If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect.
+
+Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key.
+
+<img src="img/data-driven-styling/exponential-function.png" height=344/>
+<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
+
+The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The interpolation base is `1.5`.
+
+Style function syntax:
+
+```swift
+let stops = [
+ 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 18),
+]
+
+layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
+ cameraStops: stops,
+ options: [.interpolationBase: 1.5])
+```
+
+Current syntax:
+
+```swift
+let stops = [
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
+]
+
+layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
+ stops)
+```
+
+### Interval
+
+Steps, or intervals, create a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the layout or paint value assigned to that key. You can use the `+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]` method or the custom function [`mgl_step:from:stops:`](predicates-and-expressions.html#code-mgl_step-from-stops-code) for cases where you previously used interval interpolation mode. The first parameter takes the feature attribute name and the second parameter (`from:`) optionally takes the default or fallback value for that function. The final parameter takes a stops dictionary as an argument.
+
+When we use the stops dictionary given above with an `'mgl_step:from:stops:'`, 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.
+
+Style function syntax:
+
+```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),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .interval,
+ sourceStops: stops,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+````
+
+Current syntax:
+
+```swift
+let stops: [NSNumber: UIColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+
+layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
+ UIColor.green, stops)
+```
+
+### Categorical
+
+Categorical interpolation mode took a stops dictionary. If the value for a specified feature attribute name matched one in that stops dictionary, the style value for that attribute value would be used. Categorical style functions can now be replaced with `MGL_MATCH`.
+
+`MGL_MATCH` takes an initial condition, which in this case is an attribute key. This is followed by possible matches for that key and the value to assign to the layer property if there is a match. The final argument can be a default style value that is to be used if none of the specified values match.
+
+There are three main types of events in the USGS 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.
+
+Style function syntax:
+
+```swift
+let categoricalStops = [
+ "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
+ "explosion": MGLStyleValue(rawValue: .red),
+ "quarry blast": MGLStyleValue(rawValue: .yellow),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
+ sourceStops: categoricalStops,
+ attributeName: "type",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+```
+
+Current syntax:
+```swift
+let defaultColor = UIColor.blue
+layer.circleColor = NSExpression(format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ UIColor.orange, UIColor.red, UIColor.yellow, defaultColor)
+```
+
+If your use case does not require a default value, you can either apply a predicate to your layer prior to styling it, or use the format string `"valueForKeyPath:"`.
+
+### Identity
+
+Identity interpolation mode used the attribute’s value as the style layer property value. In this example, you might set the `circleRadius` to the earthquake’s magnitude. In order to use a feature attribute value to style a layer property, set the property value to `[NSExpression expressionForKeyPath:]`, which take the feature attribute name as an argument.
+
+Style function syntax:
+
+```swift
+layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
+ sourceStops: nil,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+```
+
+Current syntax:
+```swift
+layer.circleRadius = NSExpression(forKeyPath: "mag")
+```
+
+![identity mode](img/data-driven-styling/identity.png)
+
+Some built-in functions can be applied to attribute values to style layer property values. To set the circle radius to three times the earthquake’s magnitude, create a `multiply:by:` function that takes the attribute value and the multiplier as arguments, or use a format string.
+
+```swift
+layer.circleRadius = NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
+```
+
+![multiply magnitude](img/data-driven-styling/multiply.png)
+
+You can also cast attribute values in order to use them. One example is to cast an integer as an `NSString` and use it as a text value.
+
+```swift
+let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
+magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
+mapView.style?.addLayer(magnitudeLayer)
+```
+
+![cast a value](img/data-driven-styling/cast.png)
+
+### Constant Values
+
+For constant values that do not necessarily change based on camera or attribute values, use `[NSExpression expressionForConstantValue:]` (previously `[MGLStyleValue valueWithRawValue:]`).
+
+## Resources
+
+* [USGS Earthquake Feed](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
+* [For Style Authors](for-style-authors.html)
+* [Predicates and Expressions](predicates-and-expressions.html)
diff --git a/platform/ios/docs/guides/Tile URL Templates.md b/platform/ios/docs/guides/Tile URL Templates.md
index f61d2ea33a..4c8064f781 100644
--- a/platform/ios/docs/guides/Tile URL Templates.md
+++ b/platform/ios/docs/guides/Tile URL Templates.md
@@ -4,12 +4,12 @@
-->
# Tile URL Templates
-`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource`
-objects, can be created using an initializer that accepts an array of tile URL
-templates. Tile URL templates are strings that specify the URLs of the vector
-tiles or raster tile images to load. A template resembles an absolute URL, but
-with any number of placeholder strings that the source evaluates based on the
-tile it needs to load. For example:
+`MGLTileSource` objects, specifically `MGLRasterTileSource` and
+`MGLVectorTileSource` objects, can be created using an initializer that accepts
+an array of tile URL templates. Tile URL templates are strings that specify the
+URLs of the vector tiles or raster tile images to load. A template resembles an
+absolute URL, but with any number of placeholder strings that the source
+evaluates based on the tile it needs to load. For example:
* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be
evaluated as `http://www.example.com/tiles/14/6/9.pbf`.
@@ -56,7 +56,7 @@ all of which are optional:
<td>The tile’s zoom level. At zoom level 0, each tile covers the entire
world map; at zoom level 1, it covers ¼ of the world; at zoom level 2,
<sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by
- a <code>MGLRasterSource</code> object, whether the tile zoom level
+ a <code>MGLRasterTileSource</code> object, whether the tile zoom level
matches the map’s current zoom level depends on the value of the
source’s tile size as specified in the
<code>MGLTileSourceOptionTileSize</code> key of the <code>options</code>
diff --git a/platform/ios/docs/guides/Using Style Functions at Runtime.md b/platform/ios/docs/guides/Using Style Functions at Runtime.md
deleted file mode 100644
index c1fcaa00e9..0000000000
--- a/platform/ios/docs/guides/Using Style Functions at Runtime.md
+++ /dev/null
@@ -1,162 +0,0 @@
-<!--
- This file is generated.
- Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
--->
-
-# Using Style Functions at Runtime
-
-[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
-
-Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price.
-
-_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source.
-
-You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details.
-
-![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png)
-
-This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide.
-
-A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`:
-
-* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level.
-* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake.
-* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake.
-
-The documentation for each individual style layer property notes which style functions are enabled for that property.
-
-## 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.
-
-```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),
-]
-```
-
-## Interpolation mode
-
-The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function.
-
-### Linear
-
-`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary.
-
-The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
-
-```swift
-let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
-let symbolSource = MGLSource(identifier: "source")
-let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
-
-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 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)
-mapView.style?.insertLayer(layer, below: symbolLayer)
-```
-
-![exponential mode](img/data-driven-styling/exponential.png)
-
-### Exponential
-
-By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect.
-
-Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom.
-
-<img src="img/data-driven-styling/exponential-function.png" height=344/>
-<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
-
-The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The `MGLStyleFunctionOptionInterpolationBase` is `1.5`.
-
-```swift
-let stops = [
- 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
- 14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 18),
-]
-
-layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: stops,
- options: [.interpolationBase: 1.5])
-```
-
-### Interval
-
-`MGLInterpolationModeInterval` creates a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the style value assigned to that key.
-
-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),
-]
-
-layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
-```
-
-![interval mode](img/data-driven-styling/interval.png)
-
-### Categorical
-
-At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes.
-
-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),
-]
-
-layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
-```
-
-![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
-
-### Identity
-
-`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)])
-```
-
-![identity mode](img/data-driven-styling/identity.png)
-
-##Resources
-
-* [USGS](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
-* [For Style Authors](for-style-authors.html)
diff --git a/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png b/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png
new file mode 100644
index 0000000000..b9aa946363
--- /dev/null
+++ b/platform/ios/docs/img/adding-points-to-a-map/annotation-image.png
Binary files differ
diff --git a/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png b/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png
new file mode 100644
index 0000000000..90c181f664
--- /dev/null
+++ b/platform/ios/docs/img/adding-points-to-a-map/annotation-view.png
Binary files differ
diff --git a/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png b/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png
new file mode 100644
index 0000000000..5edf88e0da
--- /dev/null
+++ b/platform/ios/docs/img/adding-points-to-a-map/circle-layer.png
Binary files differ
diff --git a/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png b/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png
new file mode 100644
index 0000000000..cff39b0bce
--- /dev/null
+++ b/platform/ios/docs/img/adding-points-to-a-map/symbol-layer.png
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif b/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif
index dee99d01fd..71d4c4a8a0 100644
--- a/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif
+++ b/platform/ios/docs/img/runtime-styling/CustomAnnotations.gif
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/DynamicStyles.gif b/platform/ios/docs/img/runtime-styling/DynamicStyles.gif
index b42d30c602..8854474ede 100644
--- a/platform/ios/docs/img/runtime-styling/DynamicStyles.gif
+++ b/platform/ios/docs/img/runtime-styling/DynamicStyles.gif
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/Emoji.gif b/platform/ios/docs/img/runtime-styling/Emoji.gif
index fc50b28972..afb7a42693 100644
--- a/platform/ios/docs/img/runtime-styling/Emoji.gif
+++ b/platform/ios/docs/img/runtime-styling/Emoji.gif
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/HexBins.gif b/platform/ios/docs/img/runtime-styling/HexBins.gif
index c810085f22..6078ac9e8d 100644
--- a/platform/ios/docs/img/runtime-styling/HexBins.gif
+++ b/platform/ios/docs/img/runtime-styling/HexBins.gif
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/Population.gif b/platform/ios/docs/img/runtime-styling/Population.gif
index 81b6c6310f..8de14e6422 100644
--- a/platform/ios/docs/img/runtime-styling/Population.gif
+++ b/platform/ios/docs/img/runtime-styling/Population.gif
Binary files differ
diff --git a/platform/ios/docs/img/runtime-styling/SnowLevels.gif b/platform/ios/docs/img/runtime-styling/SnowLevels.gif
index 8ee2f9fddd..463e0398d2 100644
--- a/platform/ios/docs/img/runtime-styling/SnowLevels.gif
+++ b/platform/ios/docs/img/runtime-styling/SnowLevels.gif
Binary files differ
diff --git a/platform/ios/docs/img/studio-workflow/add-properties.gif b/platform/ios/docs/img/studio-workflow/add-properties.gif
index 740fae655b..6cab64c050 100644
--- a/platform/ios/docs/img/studio-workflow/add-properties.gif
+++ b/platform/ios/docs/img/studio-workflow/add-properties.gif
Binary files differ
diff --git a/platform/ios/docs/img/studio-workflow/create-polygons.gif b/platform/ios/docs/img/studio-workflow/create-polygons.gif
index 6eb2c0afb8..6383dd31df 100644
--- a/platform/ios/docs/img/studio-workflow/create-polygons.gif
+++ b/platform/ios/docs/img/studio-workflow/create-polygons.gif
Binary files differ
diff --git a/platform/ios/docs/img/studio-workflow/property-values.png b/platform/ios/docs/img/studio-workflow/property-values.png
index 95704241f9..2b85db80d6 100644
--- a/platform/ios/docs/img/studio-workflow/property-values.png
+++ b/platform/ios/docs/img/studio-workflow/property-values.png
Binary files differ
diff --git a/platform/ios/docs/img/studio-workflow/stop-functions.png b/platform/ios/docs/img/studio-workflow/stop-functions.png
index 4affecf005..8480cb2d53 100644
--- a/platform/ios/docs/img/studio-workflow/stop-functions.png
+++ b/platform/ios/docs/img/studio-workflow/stop-functions.png
Binary files differ
diff --git a/platform/ios/docs/pod-README.md b/platform/ios/docs/pod-README.md
index f94073bd9f..8a3080055d 100644
--- a/platform/ios/docs/pod-README.md
+++ b/platform/ios/docs/pod-README.md
@@ -1,6 +1,6 @@
# [Mapbox Maps SDK for iOS](https://www.mapbox.com/ios-sdk/)
-The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 8.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox Maps SDK for iOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa Touch applications on iOS 9.0 and above using Objective-C, Swift, or Interface Builder. It takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
For more information, check out the [Mapbox Maps SDK for iOS homepage](https://www.mapbox.com/ios-sdk/) and the [full changelog](https://github.com/mapbox/mapbox-gl-native/blob/master/platform/ios/CHANGELOG.md) online.
@@ -10,7 +10,7 @@ For more information, check out the [Mapbox Maps SDK for iOS homepage](https://w
The Mapbox Maps SDK for iOS may be installed as either a dynamic framework or a static framework. (To reduce the download size, the static framework is omitted from some distributions; you may need to download the full package from the [release page](https://github.com/mapbox/mapbox-gl-native/releases/).)
-Integrating 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.
+Integrating the Mapbox Maps SDK for iOS requires Xcode 8.0 or higher.
{{DYNAMIC}}
diff --git a/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings
index 94e8335582..3f6104a075 100644
--- a/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/ca.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Configuració de privacitat";
+"TELEMETRY_GROUP_TITLE" = "Configuració de privacitat";
"TELEMETRY_SWITCH_TITLE" = "Telemetria Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Aquest ajust permet que l’aplicació comparteixi dades anònimes de localització i ús amb Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/da.lproj/Root.strings b/platform/ios/framework/Settings.bundle/da.lproj/Root.strings
new file mode 100644
index 0000000000..b1129a865a
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/da.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Privatlivs indstillinger";
+"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetry";
+"TELEMETRY_GROUP_FOOTER" = "Denne indstilling giver app'en tilladelse til at dele anonyme bruger data og position med Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/de.lproj/Root.strings b/platform/ios/framework/Settings.bundle/de.lproj/Root.strings
index c57189d4af..30bd2252ce 100644
--- a/platform/ios/framework/Settings.bundle/de.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/de.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Privatsphäre-Einstellungen";
+"TELEMETRY_GROUP_TITLE" = "Privatsphäre-Einstellungen";
"TELEMETRY_SWITCH_TITLE" = "Mapbox-Telemetrie";
"TELEMETRY_GROUP_FOOTER" = "Diese Einstellung erlaubt der Applikation, anonymisierte Orts- und Nutzungsdaten an Mapbox zu senden.";
diff --git a/platform/ios/framework/Settings.bundle/es.lproj/Root.strings b/platform/ios/framework/Settings.bundle/es.lproj/Root.strings
index bb0c52dcde..48da358189 100644
--- a/platform/ios/framework/Settings.bundle/es.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/es.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Ajustes de privacidad";
+"TELEMETRY_GROUP_TITLE" = "Ajustes de privacidad";
"TELEMETRY_SWITCH_TITLE" = "Telemetría Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Esta configuración permite que la aplicación comparta datos anónimos de ubicación y uso con Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings b/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings
index e0c2e50d09..b12a017e8a 100644
--- a/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/fi.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Yksityisyysasetukset";
+"TELEMETRY_GROUP_TITLE" = "Yksityisyysasetukset";
"TELEMETRY_SWITCH_TITLE" = "Mapbox-telemetria";
"TELEMETRY_GROUP_FOOTER" = "Tämä asetus antaa sovellukselle luvan jakaa anonymisoituja sijainti- ja käyttötietoja Mapboxille.";
diff --git a/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings b/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings
index c386d9846d..f00e8e2fe8 100644
--- a/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/fr.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Paramètres de confidentialité";
+"TELEMETRY_GROUP_TITLE" = "Paramètres de confidentialité";
"TELEMETRY_SWITCH_TITLE" = "Télémétrie Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Cette option permet à l’application de partager des données de localisation et d’utilisation anonymes avec Mapbox.";
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/framework/Settings.bundle/lt.lproj/Root.strings b/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings
index 832b16608b..5ae715439a 100644
--- a/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/lt.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Privatumo nustatymai";
+"TELEMETRY_GROUP_TITLE" = "Privatumo nustatymai";
"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetrija";
"TELEMETRY_GROUP_FOOTER" = "Šis nustatymas leidžia programėlei dalintis su Mapbox anonimizuota lokacija bei naudojimosi duomenimis.";
diff --git a/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings b/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings
index ea73b0ba3c..9f09002349 100644
--- a/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/nl.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Privacy Instellingen";
+"TELEMETRY_GROUP_TITLE" = "Privacy Instellingen";
"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetrie";
"TELEMETRY_GROUP_FOOTER" = "Deze instelling laat toe om anonieme locatie en gebruiksgegevens te delen met Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings b/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings
index 4cf2c71381..f273a15293 100644
--- a/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/pl.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Ustawienia prywatności";
+"TELEMETRY_GROUP_TITLE" = "Ustawienia prywatności";
"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetria";
"TELEMETRY_GROUP_FOOTER" = "Ta opcja pozwala aplikacji na anonimowe wysyłanie lokalizacji i danych do Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings b/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings
index 5b81fb66eb..f96fadd205 100644
--- a/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/pt-BR.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Configurações de privacidade";
+"TELEMETRY_GROUP_TITLE" = "Configurações de privacidade";
"TELEMETRY_SWITCH_TITLE" = "Telemetria do Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Essa configuração permite que o aplicativo compartilhe dados de localização e uso anônimos com o Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/pt-PT.lproj/Root.strings b/platform/ios/framework/Settings.bundle/pt-PT.lproj/Root.strings
new file mode 100644
index 0000000000..8e077c3d1f
--- /dev/null
+++ b/platform/ios/framework/Settings.bundle/pt-PT.lproj/Root.strings
@@ -0,0 +1,3 @@
+"TELEMETRY_GROUP_TITLE" = "Definições de Privacidade";
+"TELEMETRY_SWITCH_TITLE" = "Telemetria Mapbox";
+"TELEMETRY_GROUP_FOOTER" = "Esta definição permite à aplicação partilhar a localização e dados de utilização tornados anónimos com a Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings b/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings
index 2883ec4b87..3e37d64126 100644
--- a/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/ru.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Настройки приватности";
-"TELEMETRY_SWITCH_TITLE" = "Mapbox Телеметрия";
-"TELEMETRY_GROUP_FOOTER" = "Эта настройка разрешает приложению отправлять анонимную позицию и данные об использовании в Mapbox";
+"TELEMETRY_GROUP_TITLE" = "Настройки приватности";
+"TELEMETRY_SWITCH_TITLE" = "Телеметрия Mapbox";
+"TELEMETRY_GROUP_FOOTER" = "Эта настройка разрешает приложению отправлять обезличенные данные об использовании и местоположении в Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings b/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings
index f8324c14c4..e771af505a 100644
--- a/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/sv.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Sekretessinställningar";
+"TELEMETRY_GROUP_TITLE" = "Sekretessinställningar";
"TELEMETRY_SWITCH_TITLE" = "Mapbox Telemetri";
"TELEMETRY_GROUP_FOOTER" = "Denna inställning tillåter applikationen att dela anonymiserad plats och användningsdata med Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings b/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings
index 5d2bfd648e..b1615849f6 100644
--- a/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/uk.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Налаштування конфіденційності";
+"TELEMETRY_GROUP_TITLE" = "Налаштування конфіденційності";
"TELEMETRY_SWITCH_TITLE" = "Телеметрія Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Ці налаштування дозволяють застосунку надсилати анонімізовані дані про місце знаходження та використання даних до Mapbox.";
diff --git a/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings b/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings
index e156d9937a..29060d00a4 100644
--- a/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/vi.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "Thiết lập Quyền riêng tư";
+"TELEMETRY_GROUP_TITLE" = "Thiết lập Quyền riêng tư";
"TELEMETRY_SWITCH_TITLE" = "Trình viễn trắc Mapbox";
"TELEMETRY_GROUP_FOOTER" = "Tùy chọn này cho phép ứng dụng gửi cho Mapbox các vị trí và dữ liệu sử dụng được vô danh hóa.";
diff --git a/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings b/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings
index 3cfb1a4bad..b9e1a98325 100644
--- a/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings
+++ b/platform/ios/framework/Settings.bundle/zh-Hans.lproj/Root.strings
@@ -1,3 +1,3 @@
-"TELEMETRY_GROUP_TITLE" = "隐私设置";
+"TELEMETRY_GROUP_TITLE" = "隐私设置";
"TELEMETRY_SWITCH_TITLE" = "Mapbox传感数据";
"TELEMETRY_GROUP_FOOTER" = "此设置允许应用将用户位置和数据以匿名的方式分享给Mapbox。";
diff --git a/platform/ios/ios.xcodeproj/project.pbxproj b/platform/ios/ios.xcodeproj/project.pbxproj
index 2bc216ff1c..cb5a5587a2 100644
--- a/platform/ios/ios.xcodeproj/project.pbxproj
+++ b/platform/ios/ios.xcodeproj/project.pbxproj
@@ -12,7 +12,20 @@
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 */; };
+ 07D8C6FF1F67562C00381808 /* MGLComputedShapeSourceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */; };
+ 07D947531F67488E00E37934 /* MGLComputedShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLComputedShapeSource_Private.h */; };
+ 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 */; };
+ 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,18 +36,14 @@
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 */; };
30E5781A1DAA855E0050F07E /* UIImage+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */; };
- 350098BB1D480108004B2AF0 /* MGLVectorSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098B91D480108004B2AF0 /* MGLVectorSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 350098BC1D480108004B2AF0 /* MGLVectorSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098B91D480108004B2AF0 /* MGLVectorSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 350098BD1D480108004B2AF0 /* MGLVectorSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 350098BA1D480108004B2AF0 /* MGLVectorSource.mm */; };
- 350098BE1D480108004B2AF0 /* MGLVectorSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 350098BA1D480108004B2AF0 /* MGLVectorSource.mm */; };
+ 350098BB1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098B91D480108004B2AF0 /* MGLVectorTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 350098BC1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098B91D480108004B2AF0 /* MGLVectorTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 350098BD1D480108004B2AF0 /* MGLVectorTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 350098BA1D480108004B2AF0 /* MGLVectorTileSource.mm */; };
+ 350098BE1D480108004B2AF0 /* MGLVectorTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 350098BA1D480108004B2AF0 /* MGLVectorTileSource.mm */; };
350098DC1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */; };
350098DD1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */; };
350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */; };
@@ -43,8 +52,8 @@
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 */; };
- 3510FFF11D6D9D8C00F413B2 /* 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 */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
3510FFF91D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 3510FFF71D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.h */; };
@@ -104,10 +113,10 @@
3566C7671D4A77BA008152BC /* MGLShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C7641D4A77BA008152BC /* MGLShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C7651D4A77BA008152BC /* MGLShapeSource.mm */; };
3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C7651D4A77BA008152BC /* MGLShapeSource.mm */; };
- 3566C76C1D4A8DFA008152BC /* MGLRasterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 3566C76E1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */; };
- 3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */; };
+ 3566C76C1D4A8DFA008152BC /* MGLRasterTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C76A1D4A8DFA008152BC /* MGLRasterTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 3566C76D1D4A8DFA008152BC /* MGLRasterTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C76A1D4A8DFA008152BC /* MGLRasterTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 3566C76E1D4A8DFA008152BC /* MGLRasterTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C76B1D4A8DFA008152BC /* MGLRasterTileSource.mm */; };
+ 3566C76F1D4A8DFA008152BC /* MGLRasterTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3566C76B1D4A8DFA008152BC /* MGLRasterTileSource.mm */; };
3566C7711D4A9198008152BC /* MGLSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C7701D4A9198008152BC /* MGLSource_Private.h */; };
3566C7721D4A9198008152BC /* MGLSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 3566C7701D4A9198008152BC /* MGLSource_Private.h */; };
357579801D501E09000B822E /* MGLFillStyleLayerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */; };
@@ -121,7 +130,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 */; };
@@ -175,13 +183,77 @@
404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 404C26E11D89B877000AA13D /* MGLTileSource.mm */; };
404C26E71D89C55D000AA13D /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */; };
404C26E81D89C55D000AA13D /* MGLTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */; };
- 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */; };
- 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */; };
- 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */; };
+ 406E99B91FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */; };
+ 406E99BA1FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */; };
+ 406E99BB1FFF006C00D9FFCC /* MMEUINavigation.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */; };
+ 406E99BC1FFF006D00D9FFCC /* MMEUINavigation.m in Sources */ = {isa = PBXBuildFile; fileRef = 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */; };
+ 40834BE61FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */; };
+ 40834BE71FE05E1800C1BD0D /* MMEAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */; };
+ 40834BE81FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */; };
+ 40834BE91FE05E1800C1BD0D /* MMECommonEventData.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */; };
+ 40834BEA1FE05E1800C1BD0D /* MMEConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */; };
+ 40834BEB1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */; };
+ 40834BEC1FE05E1800C1BD0D /* MMEEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC71FE05D7000C1BD0D /* MMEEvent.m */; };
+ 40834BED1FE05E1800C1BD0D /* MMEEventLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */; };
+ 40834BEE1FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */; };
+ 40834BEF1FE05E1800C1BD0D /* MMEEventsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */; };
+ 40834BF01FE05E1800C1BD0D /* MMELocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */; };
+ 40834BF11FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */; };
+ 40834BF21FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */; };
+ 40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */; };
+ 40834BF41FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */; };
+ 40834BF51FE05E1800C1BD0D /* MMETypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */; };
+ 40834BF61FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */; };
+ 40834BF71FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */; };
+ 40834BF81FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */; };
+ 40834BF91FE05E1800C1BD0D /* MMEReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */; };
+ 40834BFA1FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */; };
+ 40834BFB1FE05E1800C1BD0D /* MMEAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */; };
+ 40834BFC1FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */; };
+ 40834BFD1FE05E1800C1BD0D /* MMECommonEventData.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */; };
+ 40834BFE1FE05E1800C1BD0D /* MMEConstants.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */; };
+ 40834BFF1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */; };
+ 40834C001FE05E1800C1BD0D /* MMEEvent.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC71FE05D7000C1BD0D /* MMEEvent.m */; };
+ 40834C011FE05E1800C1BD0D /* MMEEventLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */; };
+ 40834C021FE05E1800C1BD0D /* MMEEventsConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */; };
+ 40834C031FE05E1800C1BD0D /* MMEEventsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */; };
+ 40834C041FE05E1800C1BD0D /* MMELocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */; };
+ 40834C051FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */; };
+ 40834C061FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */; };
+ 40834C071FE05E1800C1BD0D /* MMETimerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */; };
+ 40834C081FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */; };
+ 40834C091FE05E1800C1BD0D /* MMETypes.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */; };
+ 40834C0A1FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */; };
+ 40834C0B1FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */; };
+ 40834C0C1FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */; };
+ 40834C0D1FE05E1800C1BD0D /* MMEReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */; };
+ 40834C401FE05F7500C1BD0D /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C101FE05F3600C1BD0D /* configuration_utils.m */; };
+ 40834C411FE05F7500C1BD0D /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C141FE05F3600C1BD0D /* parse_configuration.m */; };
+ 40834C421FE05F7500C1BD0D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */; };
+ 40834C431FE05F7500C1BD0D /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */; };
+ 40834C441FE05F7500C1BD0D /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */; };
+ 40834C451FE05F7500C1BD0D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */; };
+ 40834C461FE05F7500C1BD0D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */; };
+ 40834C471FE05F7500C1BD0D /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */; };
+ 40834C481FE05F7500C1BD0D /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C251FE05F3600C1BD0D /* vendor_identifier.m */; };
+ 40834C491FE05F7500C1BD0D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C271FE05F3600C1BD0D /* TrustKit.m */; };
+ 40834C4A1FE05F7500C1BD0D /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */; };
+ 40834C4B1FE05F7500C1BD0D /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */; };
+ 40834C4C1FE05F7500C1BD0D /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */; };
+ 40834C4D1FE05F7600C1BD0D /* configuration_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C101FE05F3600C1BD0D /* configuration_utils.m */; };
+ 40834C4E1FE05F7600C1BD0D /* parse_configuration.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C141FE05F3600C1BD0D /* parse_configuration.m */; };
+ 40834C4F1FE05F7600C1BD0D /* ssl_pin_verifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */; };
+ 40834C501FE05F7600C1BD0D /* TSKSPKIHashCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */; };
+ 40834C511FE05F7600C1BD0D /* reporting_utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */; };
+ 40834C521FE05F7600C1BD0D /* TSKBackgroundReporter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */; };
+ 40834C531FE05F7600C1BD0D /* TSKPinFailureReport.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */; };
+ 40834C541FE05F7600C1BD0D /* TSKReportsRateLimiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */; };
+ 40834C551FE05F7600C1BD0D /* vendor_identifier.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C251FE05F3600C1BD0D /* vendor_identifier.m */; };
+ 40834C561FE05F7600C1BD0D /* TrustKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C271FE05F3600C1BD0D /* TrustKit.m */; };
+ 40834C571FE05F7600C1BD0D /* TSKPinningValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */; };
+ 40834C581FE05F7600C1BD0D /* TSKPinningValidatorResult.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */; };
+ 40834C591FE05F7600C1BD0D /* TSKTrustKitConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */; };
4085AF091D933DEA00F11B22 /* MGLTileSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */; };
- 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */; };
- 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */; };
- 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */ = {isa = PBXBuildFile; fileRef = 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */; };
408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */; };
408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; };
408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */; };
@@ -189,10 +261,6 @@
409F43FD1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 409F43FC1E9E781C0048729D /* MGLMapViewDelegateIntegrationTests.swift */; };
40CF6DBB1DAC3C6600A4D18B /* MGLShape_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 40CF6DBA1DAC3C1800A4D18B /* MGLShape_Private.h */; };
40CFA6511D7875BB008103BD /* MGLShapeSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */; };
- 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */; };
- 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */; };
- 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */; };
- 40EA6BC41EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */ = {isa = PBXBuildFile; fileRef = 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */; };
40EDA1C01CFE0E0200D9EA68 /* MGLAnnotationContainerView.h in Headers */ = {isa = PBXBuildFile; fileRef = 40EDA1BD1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.h */; };
40EDA1C11CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
40EDA1C21CFE0E0500D9EA68 /* MGLAnnotationContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 40EDA1BE1CFE0D4A00D9EA68 /* MGLAnnotationContainerView.m */; };
@@ -216,14 +284,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 +296,20 @@
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 */; };
+ 9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */; };
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,17 +318,55 @@
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 /* MGLComputedShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D9474E1F67487E00E37934 /* MGLComputedShapeSource_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, ); }; };
+ 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 */; };
+ 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 */; };
- AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; };
- AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */; };
- AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; };
- AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */ = {isa = PBXBuildFile; fileRef = AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */; };
AC518DFF201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E00201BB55A00EBC820 /* MGLTelemetryConfig.h in Headers */ = {isa = PBXBuildFile; fileRef = AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */; };
AC518E03201BB56000EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; };
AC518E04201BB56100EBC820 /* MGLTelemetryConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */; };
+ CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.m in Sources */ = {isa = PBXBuildFile; fileRef = CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */; };
CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
CA55CD42202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */ = {isa = PBXBuildFile; fileRef = CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ CAA69DA4206DCD0E007279CD /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA4A26961CB6E795000B7809 /* Mapbox.framework */; };
+ CAA69DA5206DCD0E007279CD /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA4A26961CB6E795000B7809 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ CABE5DAD2072FAB40003AF3C /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
DA00FC8E1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC8F1D5EEB0D009AABC8 /* MGLAttributionInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */; };
@@ -274,7 +384,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 */; };
@@ -374,12 +483,8 @@
DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA88484F1CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */; };
DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; };
- DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */; };
- DA8848521CBAFB9800AB86E3 /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; };
DA8848531CBAFB9800AB86E3 /* MGLCompactCalloutView.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */; };
DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
- DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */; };
- DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; };
DA8848571CBAFB9800AB86E3 /* MGLMapboxEvents.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */; };
DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; };
DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; };
@@ -394,7 +499,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 */; };
@@ -406,6 +510,8 @@
DA8963381CC549A100684375 /* sprites in Resources */ = {isa = PBXBuildFile; fileRef = DA8963341CC549A100684375 /* sprites */; };
DA8963391CC549A100684375 /* styles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963351CC549A100684375 /* styles */; };
DA89633A1CC549A100684375 /* tiles in Resources */ = {isa = PBXBuildFile; fileRef = DA8963361CC549A100684375 /* tiles */; };
+ DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */; };
DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 3557F7AF1E1D27D300CCA5E6 /* MGLDistanceFormatter.m */; };
DAA4E4081CBB6C9500178DFB /* Mapbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; };
DAA4E4091CBB6C9500178DFB /* Mapbox.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DA8847D21CBAF91600AB86E3 /* Mapbox.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@@ -426,9 +532,7 @@
DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848161CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m */; };
DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848181CBAFA6200AB86E3 /* NSString+MGLAdditions.m */; };
DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */; };
- DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */; };
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */; };
- DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */; };
DAA4E4311CBB730400178DFB /* MGLMapboxEvents.m in Sources */ = {isa = PBXBuildFile; fileRef = DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */; };
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */; };
DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */ = {isa = PBXBuildFile; fileRef = DA88484C1CBAFB9800AB86E3 /* MGLUserLocation.m */; };
@@ -467,8 +571,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 */; };
@@ -484,12 +594,17 @@
DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAED38621D62D0FC00D7640F /* NSURL+MGLAdditions.m */; };
DAED38661D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = DAED38621D62D0FC00D7640F /* NSURL+MGLAdditions.m */; };
DAEDC4341D603417000224FF /* MGLAttributionInfoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */; };
- DAF0D8101DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */; };
- DAF0D8111DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */; };
- DAF0D8131DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */; };
- DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */; };
+ DAF0D8101DFE0EA000B28378 /* MGLRasterTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80F1DFE0EA000B28378 /* MGLRasterTileSource_Private.h */; };
+ DAF0D8111DFE0EA000B28378 /* MGLRasterTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D80F1DFE0EA000B28378 /* MGLRasterTileSource_Private.h */; };
+ DAF0D8131DFE0EC500B28378 /* MGLVectorTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8121DFE0EC500B28378 /* MGLVectorTileSource_Private.h */; };
+ DAF0D8141DFE0EC500B28378 /* MGLVectorTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DAF0D8121DFE0EC500B28378 /* MGLVectorTileSource_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 */; };
@@ -509,6 +624,20 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
+ 165D0CE520005351009A3C66 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = DA8847D11CBAF91600AB86E3;
+ remoteInfo = dynamic;
+ };
+ CABE5DAB2072FA660003AF3C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 16376B2E1FFDB4B40000563E;
+ remoteInfo = "Integration Test Harness";
+ };
DA25D5C71CCDA0C100607828 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = DA1DC9421CB6C1C2006E619F /* Project object */;
@@ -554,6 +683,17 @@
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
+ CAA69DA6206DCD0E007279CD /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ CAA69DA5206DCD0E007279CD /* Mapbox.framework in Embed Frameworks */,
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA4A269A1CB6F5D3000B7809 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
@@ -591,22 +731,36 @@
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 /* MGLComputedShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource_Private.h; 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>"; };
30E578111DAA7D690050F07E /* UIImage+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+MGLAdditions.h"; path = "src/UIImage+MGLAdditions.h"; sourceTree = SOURCE_ROOT; };
30E578121DAA7D690050F07E /* UIImage+MGLAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "UIImage+MGLAdditions.mm"; path = "src/UIImage+MGLAdditions.mm"; sourceTree = SOURCE_ROOT; };
- 350098B91D480108004B2AF0 /* MGLVectorSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource.h; sourceTree = "<group>"; };
- 350098BA1D480108004B2AF0 /* MGLVectorSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorSource.mm; sourceTree = "<group>"; };
+ 350098B91D480108004B2AF0 /* MGLVectorTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorTileSource.h; sourceTree = "<group>"; };
+ 350098BA1D480108004B2AF0 /* MGLVectorTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorTileSource.mm; sourceTree = "<group>"; };
350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLStyleAttributeAdditions.h"; sourceTree = "<group>"; };
350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSValue+MGLStyleAttributeAdditions.mm"; sourceTree = "<group>"; };
3510FFE81D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSComparisonPredicate+MGLAdditions.h"; sourceTree = "<group>"; };
@@ -646,8 +800,8 @@
355ADFFC1E9281DA00F3939D /* MGLScaleBar.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLScaleBar.mm; sourceTree = "<group>"; };
3566C7641D4A77BA008152BC /* MGLShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource.h; sourceTree = "<group>"; };
3566C7651D4A77BA008152BC /* MGLShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeSource.mm; sourceTree = "<group>"; };
- 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource.h; sourceTree = "<group>"; };
- 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterSource.mm; sourceTree = "<group>"; };
+ 3566C76A1D4A8DFA008152BC /* MGLRasterTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterTileSource.h; sourceTree = "<group>"; };
+ 3566C76B1D4A8DFA008152BC /* MGLRasterTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterTileSource.mm; sourceTree = "<group>"; };
3566C7701D4A9198008152BC /* MGLSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSource_Private.h; sourceTree = "<group>"; };
3575797F1D501E09000B822E /* MGLFillStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLFillStyleLayerTests.mm; path = ../../darwin/test/MGLFillStyleLayerTests.mm; sourceTree = "<group>"; };
357579821D502AE6000B822E /* MGLRasterStyleLayerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLRasterStyleLayerTests.mm; path = ../../darwin/test/MGLRasterStyleLayerTests.mm; sourceTree = "<group>"; };
@@ -655,12 +809,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>"; };
@@ -685,30 +837,103 @@
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>"; };
- 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>"; };
+ 404C26E61D89C515000AA13D /* MGLTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLTileSource_Private.h; sourceTree = "<group>"; };
+ 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventLogReportViewController.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogReportViewController.m"; sourceTree = SOURCE_ROOT; };
+ 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUINavigation.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUINavigation.m"; sourceTree = SOURCE_ROOT; };
+ 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUINavigation.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUINavigation.h"; sourceTree = SOURCE_ROOT; };
+ 406E99B51FFEFED600D9FFCC /* MMEEventLogReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventLogReportViewController.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogReportViewController.h"; sourceTree = SOURCE_ROOT; };
+ 40834AEF1FDF4F0100C1BD0D /* Mapbox-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Mapbox-Prefix.pch"; path = "src/Mapbox-Prefix.pch"; sourceTree = SOURCE_ROOT; };
+ 40834BA31FE05D6B00C1BD0D /* MMEEventsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventsManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsManager.h"; sourceTree = SOURCE_ROOT; };
+ 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventsManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsManager.m"; sourceTree = SOURCE_ROOT; };
+ 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEAPIClient.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEAPIClient.m"; sourceTree = SOURCE_ROOT; };
+ 40834BA61FE05D6B00C1BD0D /* MMEEventLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventLogger.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogger.h"; sourceTree = SOURCE_ROOT; };
+ 40834BA71FE05D6B00C1BD0D /* MMETrustKitWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETrustKitWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETrustKitWrapper.h"; sourceTree = SOURCE_ROOT; };
+ 40834BAA1FE05D6C00C1BD0D /* MMENSURLSessionWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENSURLSessionWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSURLSessionWrapper.h"; sourceTree = SOURCE_ROOT; };
+ 40834BAB1FE05D6C00C1BD0D /* MMEAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEAPIClient.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEAPIClient.h"; sourceTree = SOURCE_ROOT; };
+ 40834BAC1FE05D6C00C1BD0D /* MapboxMobileEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MapboxMobileEvents.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MapboxMobileEvents.h"; sourceTree = SOURCE_ROOT; };
+ 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUniqueIdentifier.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUniqueIdentifier.m"; sourceTree = SOURCE_ROOT; };
+ 40834BAE1FE05D6C00C1BD0D /* MMECommonEventData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMECommonEventData.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECommonEventData.h"; sourceTree = SOURCE_ROOT; };
+ 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSData+MMEGZIP.m"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/NSData+MMEGZIP.m"; sourceTree = SOURCE_ROOT; };
+ 40834BB01FE05D6C00C1BD0D /* MMEConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEConstants.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEConstants.h"; sourceTree = SOURCE_ROOT; };
+ 40834BB11FE05D6D00C1BD0D /* MMEDependencyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEDependencyManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEDependencyManager.h"; sourceTree = SOURCE_ROOT; };
+ 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventsConfiguration.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsConfiguration.m"; sourceTree = SOURCE_ROOT; };
+ 40834BB31FE05D6D00C1BD0D /* MMELocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMELocationManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMELocationManager.h"; sourceTree = SOURCE_ROOT; };
+ 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEDependencyManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEDependencyManager.m"; sourceTree = SOURCE_ROOT; };
+ 40834BB51FE05D6D00C1BD0D /* MMECategoryLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMECategoryLoader.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECategoryLoader.h"; sourceTree = SOURCE_ROOT; };
+ 40834BB61FE05D6D00C1BD0D /* MMETypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETypes.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETypes.h"; sourceTree = SOURCE_ROOT; };
+ 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEventLogger.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventLogger.m"; sourceTree = SOURCE_ROOT; };
+ 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMELocationManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMELocationManager.m"; sourceTree = SOURCE_ROOT; };
+ 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETimerManager.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.m"; sourceTree = SOURCE_ROOT; };
+ 40834BBA1FE05D6E00C1BD0D /* MMEEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEvent.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEvent.h"; sourceTree = SOURCE_ROOT; };
+ 40834BBB1FE05D6E00C1BD0D /* MMEEventsConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEEventsConfiguration.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEventsConfiguration.h"; sourceTree = SOURCE_ROOT; };
+ 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMENSDateWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSDateWrapper.m"; sourceTree = SOURCE_ROOT; };
+ 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETypes.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETypes.m"; sourceTree = SOURCE_ROOT; };
+ 40834BBE1FE05D6E00C1BD0D /* MMEUIApplicationWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUIApplicationWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUIApplicationWrapper.h"; sourceTree = SOURCE_ROOT; };
+ 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEUniqueIdentifier.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUniqueIdentifier.h"; sourceTree = SOURCE_ROOT; };
+ 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEConstants.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEConstants.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMETrustKitWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETrustKitWrapper.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC21FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "CLLocation+MMEMobileEvents.h"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/CLLocation+MMEMobileEvents.h"; sourceTree = SOURCE_ROOT; };
+ 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "CLLocation+MMEMobileEvents.m"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/CLLocation+MMEMobileEvents.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMECategoryLoader.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECategoryLoader.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC51FE05D6F00C1BD0D /* MMENSDateWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENSDateWrapper.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSDateWrapper.h"; sourceTree = SOURCE_ROOT; };
+ 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMENSURLSessionWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENSURLSessionWrapper.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC71FE05D7000C1BD0D /* MMEEvent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEEvent.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEEvent.m"; sourceTree = SOURCE_ROOT; };
+ 40834BC81FE05D7000C1BD0D /* MMENamespacedDependencies.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMENamespacedDependencies.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMENamespacedDependencies.h"; sourceTree = SOURCE_ROOT; };
+ 40834BC91FE05D7000C1BD0D /* MMETimerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMETimerManager.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMETimerManager.h"; sourceTree = SOURCE_ROOT; };
+ 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEUIApplicationWrapper.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMEUIApplicationWrapper.m"; sourceTree = SOURCE_ROOT; };
+ 40834BCC1FE05D7100C1BD0D /* MMEReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MMEReachability.h; path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability/MMEReachability.h"; sourceTree = SOURCE_ROOT; };
+ 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMEReachability.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability/MMEReachability.m"; sourceTree = SOURCE_ROOT; };
+ 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MMECommonEventData.m; path = "vendor/mapbox-events-ios/MapboxMobileEvents/MMECommonEventData.m"; sourceTree = SOURCE_ROOT; };
+ 40834BCF1FE05D7100C1BD0D /* NSData+MMEGZIP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSData+MMEGZIP.h"; path = "vendor/mapbox-events-ios/MapboxMobileEvents/NSData+MMEGZIP.h"; sourceTree = SOURCE_ROOT; };
+ 40834C0F1FE05F3600C1BD0D /* configuration_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = configuration_utils.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/configuration_utils.h"; sourceTree = SOURCE_ROOT; };
+ 40834C101FE05F3600C1BD0D /* configuration_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = configuration_utils.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/configuration_utils.m"; sourceTree = SOURCE_ROOT; };
+ 40834C131FE05F3600C1BD0D /* parse_configuration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = parse_configuration.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/parse_configuration.h"; sourceTree = SOURCE_ROOT; };
+ 40834C141FE05F3600C1BD0D /* parse_configuration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = parse_configuration.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/parse_configuration.m"; sourceTree = SOURCE_ROOT; };
+ 40834C161FE05F3600C1BD0D /* ssl_pin_verifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ssl_pin_verifier.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/ssl_pin_verifier.h"; sourceTree = SOURCE_ROOT; };
+ 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = ssl_pin_verifier.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/ssl_pin_verifier.m"; sourceTree = SOURCE_ROOT; };
+ 40834C181FE05F3600C1BD0D /* TSKPublicKeyAlgorithm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPublicKeyAlgorithm.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKPublicKeyAlgorithm.h"; sourceTree = SOURCE_ROOT; };
+ 40834C191FE05F3600C1BD0D /* TSKSPKIHashCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKSPKIHashCache.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKSPKIHashCache.h"; sourceTree = SOURCE_ROOT; };
+ 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKSPKIHashCache.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning/TSKSPKIHashCache.m"; sourceTree = SOURCE_ROOT; };
+ 40834C1C1FE05F3600C1BD0D /* reporting_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = reporting_utils.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/reporting_utils.h"; sourceTree = SOURCE_ROOT; };
+ 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = reporting_utils.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/reporting_utils.m"; sourceTree = SOURCE_ROOT; };
+ 40834C1E1FE05F3600C1BD0D /* TSKBackgroundReporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKBackgroundReporter.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKBackgroundReporter.h"; sourceTree = SOURCE_ROOT; };
+ 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKBackgroundReporter.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKBackgroundReporter.m"; sourceTree = SOURCE_ROOT; };
+ 40834C201FE05F3600C1BD0D /* TSKPinFailureReport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinFailureReport.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKPinFailureReport.h"; sourceTree = SOURCE_ROOT; };
+ 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinFailureReport.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKPinFailureReport.m"; sourceTree = SOURCE_ROOT; };
+ 40834C221FE05F3600C1BD0D /* TSKReportsRateLimiter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKReportsRateLimiter.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKReportsRateLimiter.h"; sourceTree = SOURCE_ROOT; };
+ 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKReportsRateLimiter.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/TSKReportsRateLimiter.m"; sourceTree = SOURCE_ROOT; };
+ 40834C241FE05F3600C1BD0D /* vendor_identifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vendor_identifier.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/vendor_identifier.h"; sourceTree = SOURCE_ROOT; };
+ 40834C251FE05F3600C1BD0D /* vendor_identifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = vendor_identifier.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting/vendor_identifier.m"; sourceTree = SOURCE_ROOT; };
+ 40834C261FE05F3600C1BD0D /* TrustKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TrustKit.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TrustKit.h"; sourceTree = SOURCE_ROOT; };
+ 40834C271FE05F3600C1BD0D /* TrustKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TrustKit.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TrustKit.m"; sourceTree = SOURCE_ROOT; };
+ 40834C281FE05F3600C1BD0D /* TSKLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKLog.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKLog.h"; sourceTree = SOURCE_ROOT; };
+ 40834C291FE05F3600C1BD0D /* TSKPinningValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidator.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator.h"; sourceTree = SOURCE_ROOT; };
+ 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinningValidator.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator.m"; sourceTree = SOURCE_ROOT; };
+ 40834C2B1FE05F3600C1BD0D /* TSKPinningValidator_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidator_Private.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidator_Private.h"; sourceTree = SOURCE_ROOT; };
+ 40834C2C1FE05F3600C1BD0D /* TSKPinningValidatorCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidatorCallback.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorCallback.h"; sourceTree = SOURCE_ROOT; };
+ 40834C2D1FE05F3600C1BD0D /* TSKPinningValidatorResult.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKPinningValidatorResult.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorResult.h"; sourceTree = SOURCE_ROOT; };
+ 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKPinningValidatorResult.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKPinningValidatorResult.m"; sourceTree = SOURCE_ROOT; };
+ 40834C2F1FE05F3600C1BD0D /* TSKTrustDecision.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKTrustDecision.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustDecision.h"; sourceTree = SOURCE_ROOT; };
+ 40834C301FE05F3600C1BD0D /* TSKTrustKitConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TSKTrustKitConfig.h; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustKitConfig.h"; sourceTree = SOURCE_ROOT; };
+ 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TSKTrustKitConfig.m; path = "vendor/mapbox-events-ios/vendor/TrustKit/TSKTrustKitConfig.m"; sourceTree = SOURCE_ROOT; };
4085AF081D933DEA00F11B22 /* MGLTileSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MGLTileSetTests.mm; path = ../../darwin/test/MGLTileSetTests.mm; sourceTree = "<group>"; };
408AA8551DAEDA0800022900 /* NSDictionary+MGLAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+MGLAdditions.h"; sourceTree = "<group>"; };
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; };
@@ -727,19 +952,23 @@
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>"; };
+ 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewScaleBarTests.m; 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>"; };
@@ -760,10 +989,10 @@
96E0272D1E57C7E6004B8E66 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Localizable.strings; sourceTree = "<group>"; };
96E0272E1E57C7E7004B8E66 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
96F3F73B1F5711F1003E2D2C /* MGLUserLocationHeadingIndicator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLUserLocationHeadingIndicator.h; sourceTree = "<group>"; };
- AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-geotrust_2018.der"; sourceTree = "<group>"; };
- AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */ = {isa = PBXFileReference; lastKnownFileType = file; path = "api_mapbox_cn-digicert_2018.der"; sourceTree = "<group>"; };
AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLTelemetryConfig.h; sourceTree = "<group>"; };
AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLTelemetryConfig.m; sourceTree = "<group>"; };
+ CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MGLMapViewIntegrationTest.m; sourceTree = "<group>"; };
+ CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MGLMapViewIntegrationTest.h; sourceTree = "<group>"; };
CA55CD3E202C16AA00CE7095 /* MGLCameraChangeReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCameraChangeReason.h; sourceTree = "<group>"; };
DA00FC8C1D5EEB0D009AABC8 /* MGLAttributionInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAttributionInfo.h; sourceTree = "<group>"; };
DA00FC8D1D5EEB0D009AABC8 /* MGLAttributionInfo.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLAttributionInfo.mm; sourceTree = "<group>"; };
@@ -788,7 +1017,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>"; };
@@ -920,12 +1148,8 @@
DA8848391CBAFB8500AB86E3 /* MGLUserLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLUserLocation.h; sourceTree = "<group>"; };
DA8848401CBAFB9800AB86E3 /* MGLAnnotationImage_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAnnotationImage_Private.h; sourceTree = "<group>"; };
DA8848411CBAFB9800AB86E3 /* MGLAnnotationImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAnnotationImage.m; sourceTree = "<group>"; };
- DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLAPIClient.h; sourceTree = "<group>"; };
- DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLAPIClient.m; sourceTree = "<group>"; };
DA8848441CBAFB9800AB86E3 /* MGLCompactCalloutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCompactCalloutView.h; sourceTree = "<group>"; };
DA8848451CBAFB9800AB86E3 /* MGLCompactCalloutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLCompactCalloutView.m; sourceTree = "<group>"; };
- DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLLocationManager.h; sourceTree = "<group>"; };
- DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLLocationManager.m; sourceTree = "<group>"; };
DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLMapboxEvents.h; sourceTree = "<group>"; };
DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MGLMapboxEvents.m; sourceTree = "<group>"; };
DA88484A1CBAFB9800AB86E3 /* MGLMapView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLMapView.mm; sourceTree = "<group>"; };
@@ -940,8 +1164,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>"; };
@@ -955,10 +1177,16 @@
DA8F25B91D51D2570010E6B5 /* MGLStyleLayerTests.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.mm.ejs; path = ../test/MGLStyleLayerTests.mm.ejs; sourceTree = "<group>"; };
DA8F25BA1D51D2570010E6B5 /* MGLStyleLayer.h.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.h.ejs; sourceTree = "<group>"; };
DA8F25BB1D51D2570010E6B5 /* MGLStyleLayer.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.mm.ejs; sourceTree = "<group>"; };
+ DA93409B208562EB0059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DA93409C2085630C0059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Foundation.stringsdict"; sourceTree = "<group>"; };
+ DA93409D208563220059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DA93409E208563360059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
+ DA93409F208563440059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Root.strings"; sourceTree = "<group>"; };
DA9C012B1E4C7AD900C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Localizable.stringsdict"; sourceTree = "<group>"; };
DA9C012C1E4C7ADB00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Foundation.stringsdict"; sourceTree = "<group>"; };
DA9C012D1E4C7B1F00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
DA9C012E1E4C7B6100C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Root.strings"; sourceTree = "<group>"; };
+ DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSExpression+MGLAdditions.h"; sourceTree = "<group>"; };
DAA32CA11E4C44DB006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DAA32CA21E4C44DD006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = de; path = de.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
DAA32CA31E4C44F1006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Foundation.strings; sourceTree = "<group>"; };
@@ -992,7 +1220,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>"; };
@@ -1000,6 +1235,12 @@
DAD1656B1CF41981001FF4B9 /* MGLFeature.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLFeature.mm; sourceTree = "<group>"; };
DAD165761CF4CDFF001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = "<group>"; };
DAD165771CF4CDFF001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = "<group>"; };
+ DAD88E07202ACFE800AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAD88E08202AD01300AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DAD88E09202AD01F00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DAD88E0A202AD03C00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAD88E0B202AD04D00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+ DAD88E0C202AD06500AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Root.strings; sourceTree = "<group>"; };
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLNSStringAdditionsTests.m; path = ../../darwin/test/MGLNSStringAdditionsTests.m; sourceTree = "<group>"; };
DAE8CCAD1E6E8C70009B5CB0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
DAE8CCAE1E6E8C76009B5CB0 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Root.strings; sourceTree = "<group>"; };
@@ -1007,9 +1248,12 @@
DAED38611D62D0FC00D7640F /* NSURL+MGLAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+MGLAdditions.h"; sourceTree = "<group>"; };
DAED38621D62D0FC00D7640F /* NSURL+MGLAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+MGLAdditions.m"; sourceTree = "<group>"; };
DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MGLAttributionInfoTests.m; path = ../../darwin/test/MGLAttributionInfoTests.m; sourceTree = "<group>"; };
- 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>"; };
+ DAF0D80F1DFE0EA000B28378 /* MGLRasterTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterTileSource_Private.h; sourceTree = "<group>"; };
+ DAF0D8121DFE0EC500B28378 /* MGLVectorTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorTileSource_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>"; };
@@ -1027,6 +1271,22 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 16376B041FFD9DAF0000563E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CABE5DAD2072FAB40003AF3C /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 16376B2C1FFDB4B40000563E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ CAA69DA4206DCD0E007279CD /* Mapbox.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
DA1DC9471CB6C1C2006E619F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1078,14 +1338,43 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 16376B081FFD9DAF0000563E /* Integration Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 16376B091FFD9DAF0000563E /* MBGLIntegrationTests.m */,
+ 16376B0B1FFD9DAF0000563E /* Info.plist */,
+ CA0C27932076CA19001CE5B7 /* MGLMapViewIntegrationTest.m */,
+ CA0C27952076CA50001CE5B7 /* MGLMapViewIntegrationTest.h */,
+ );
+ 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 = (
+ 0778DD401F67555F00A73B34 /* MGLComputedShapeSource.h */,
+ 07D9474E1F67487E00E37934 /* MGLComputedShapeSource_Private.h */,
+ 0778DD411F67555F00A73B34 /* MGLComputedShapeSource.mm */,
071BBAFC1EE75CD4001FB02A /* MGLImageSource.h */,
071BBAFD1EE75CD4001FB02A /* MGLImageSource.mm */,
- 3566C76A1D4A8DFA008152BC /* MGLRasterSource.h */,
- DAF0D80F1DFE0EA000B28378 /* MGLRasterSource_Private.h */,
- 3566C76B1D4A8DFA008152BC /* MGLRasterSource.mm */,
+ 3566C76A1D4A8DFA008152BC /* MGLRasterTileSource.h */,
+ DAF0D80F1DFE0EA000B28378 /* MGLRasterTileSource_Private.h */,
+ 3566C76B1D4A8DFA008152BC /* MGLRasterTileSource.mm */,
+ DACA86242019218500E9693A /* MGLRasterDEMSource.h */,
+ DACA86252019218500E9693A /* MGLRasterDEMSource.mm */,
3566C7641D4A77BA008152BC /* MGLShapeSource.h */,
40F8876F1D7A1DB8008ECB67 /* MGLShapeSource_Private.h */,
3566C7651D4A77BA008152BC /* MGLShapeSource.mm */,
@@ -1095,9 +1384,9 @@
404C26E01D89B877000AA13D /* MGLTileSource.h */,
404C26E61D89C515000AA13D /* MGLTileSource_Private.h */,
404C26E11D89B877000AA13D /* MGLTileSource.mm */,
- 350098B91D480108004B2AF0 /* MGLVectorSource.h */,
- DAF0D8121DFE0EC500B28378 /* MGLVectorSource_Private.h */,
- 350098BA1D480108004B2AF0 /* MGLVectorSource.mm */,
+ 350098B91D480108004B2AF0 /* MGLVectorTileSource.h */,
+ DAF0D8121DFE0EC500B28378 /* MGLVectorTileSource_Private.h */,
+ 350098BA1D480108004B2AF0 /* MGLVectorTileSource.mm */,
);
name = Sources;
sourceTree = "<group>";
@@ -1112,9 +1401,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 */,
@@ -1152,8 +1445,6 @@
35599DB81D46AD7F0048254D /* Categories */ = {
isa = PBXGroup;
children = (
- 1FDD9D6D1F26936400252B09 /* MGLVectorSource+MGLAdditions.h */,
- 1FDD9D6E1F26936400252B09 /* MGLVectorSource+MGLAdditions.m */,
350098DA1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.h */,
350098DB1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm */,
);
@@ -1175,9 +1466,6 @@
3575798F1D513EF1000B822E /* Layers */,
40CFA64E1D78754A008103BD /* Sources */,
1F7454A61ED08AB400021D39 /* MGLLightTest.mm */,
- 357F09091DF84F3800941873 /* MGLStyleValueTests.h */,
- 3599A3E51DF708BC00E77FB2 /* MGLStyleValueTests.m */,
- DA2207BE1DC0805F0002F84D /* MGLStyleValueTests.swift */,
);
name = Styling;
sourceTree = "<group>";
@@ -1185,11 +1473,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 */,
@@ -1204,6 +1495,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 */,
@@ -1230,6 +1523,137 @@
name = "Test Helpers";
sourceTree = "<group>";
};
+ 40834BA11FE05CFD00C1BD0D /* Development */ = {
+ isa = PBXGroup;
+ children = (
+ 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */,
+ 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */,
+ );
+ name = Development;
+ sourceTree = "<group>";
+ };
+ 40834BA21FE05D3100C1BD0D /* Runtime */ = {
+ isa = PBXGroup;
+ children = (
+ 40834BC81FE05D7000C1BD0D /* MMENamespacedDependencies.h */,
+ 40834BAC1FE05D6C00C1BD0D /* MapboxMobileEvents.h */,
+ DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */,
+ DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */,
+ 40834BC21FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.h */,
+ 40834BC31FE05D6F00C1BD0D /* CLLocation+MMEMobileEvents.m */,
+ 40834BAB1FE05D6C00C1BD0D /* MMEAPIClient.h */,
+ 40834BA51FE05D6B00C1BD0D /* MMEAPIClient.m */,
+ 40834BB51FE05D6D00C1BD0D /* MMECategoryLoader.h */,
+ 40834BC41FE05D6F00C1BD0D /* MMECategoryLoader.m */,
+ 40834BAE1FE05D6C00C1BD0D /* MMECommonEventData.h */,
+ 40834BCE1FE05D7100C1BD0D /* MMECommonEventData.m */,
+ 40834BB01FE05D6C00C1BD0D /* MMEConstants.h */,
+ 40834BC01FE05D6E00C1BD0D /* MMEConstants.m */,
+ 40834BB11FE05D6D00C1BD0D /* MMEDependencyManager.h */,
+ 40834BB41FE05D6D00C1BD0D /* MMEDependencyManager.m */,
+ 40834BBA1FE05D6E00C1BD0D /* MMEEvent.h */,
+ 40834BC71FE05D7000C1BD0D /* MMEEvent.m */,
+ 40834BA61FE05D6B00C1BD0D /* MMEEventLogger.h */,
+ 40834BB71FE05D6D00C1BD0D /* MMEEventLogger.m */,
+ 406E99B51FFEFED600D9FFCC /* MMEEventLogReportViewController.h */,
+ 406E99B11FFEFED500D9FFCC /* MMEEventLogReportViewController.m */,
+ 40834BBB1FE05D6E00C1BD0D /* MMEEventsConfiguration.h */,
+ 40834BB21FE05D6D00C1BD0D /* MMEEventsConfiguration.m */,
+ 40834BA31FE05D6B00C1BD0D /* MMEEventsManager.h */,
+ 40834BA41FE05D6B00C1BD0D /* MMEEventsManager.m */,
+ 40834BB31FE05D6D00C1BD0D /* MMELocationManager.h */,
+ 40834BB81FE05D6D00C1BD0D /* MMELocationManager.m */,
+ 40834BC51FE05D6F00C1BD0D /* MMENSDateWrapper.h */,
+ 40834BBC1FE05D6E00C1BD0D /* MMENSDateWrapper.m */,
+ 40834BAA1FE05D6C00C1BD0D /* MMENSURLSessionWrapper.h */,
+ 40834BC61FE05D7000C1BD0D /* MMENSURLSessionWrapper.m */,
+ 40834BC91FE05D7000C1BD0D /* MMETimerManager.h */,
+ 40834BB91FE05D6E00C1BD0D /* MMETimerManager.m */,
+ 40834BA71FE05D6B00C1BD0D /* MMETrustKitWrapper.h */,
+ 40834BC11FE05D6F00C1BD0D /* MMETrustKitWrapper.m */,
+ 40834BB61FE05D6D00C1BD0D /* MMETypes.h */,
+ 40834BBD1FE05D6E00C1BD0D /* MMETypes.m */,
+ 40834BBE1FE05D6E00C1BD0D /* MMEUIApplicationWrapper.h */,
+ 40834BCA1FE05D7000C1BD0D /* MMEUIApplicationWrapper.m */,
+ 406E99B31FFEFED600D9FFCC /* MMEUINavigation.h */,
+ 406E99B21FFEFED500D9FFCC /* MMEUINavigation.m */,
+ 40834BBF1FE05D6E00C1BD0D /* MMEUniqueIdentifier.h */,
+ 40834BAD1FE05D6C00C1BD0D /* MMEUniqueIdentifier.m */,
+ 40834BCF1FE05D7100C1BD0D /* NSData+MMEGZIP.h */,
+ 40834BAF1FE05D6C00C1BD0D /* NSData+MMEGZIP.m */,
+ 40834BCB1FE05D7100C1BD0D /* Reachability */,
+ 40834C0E1FE05F3600C1BD0D /* TrustKit */,
+ );
+ name = Runtime;
+ sourceTree = "<group>";
+ };
+ 40834BCB1FE05D7100C1BD0D /* Reachability */ = {
+ isa = PBXGroup;
+ children = (
+ 40834BCC1FE05D7100C1BD0D /* MMEReachability.h */,
+ 40834BCD1FE05D7100C1BD0D /* MMEReachability.m */,
+ );
+ name = Reachability;
+ path = "vendor/mapbox-events-ios/MapboxMobileEvents/Reachability";
+ sourceTree = SOURCE_ROOT;
+ };
+ 40834C0E1FE05F3600C1BD0D /* TrustKit */ = {
+ isa = PBXGroup;
+ children = (
+ 40834C0F1FE05F3600C1BD0D /* configuration_utils.h */,
+ 40834C101FE05F3600C1BD0D /* configuration_utils.m */,
+ 40834C131FE05F3600C1BD0D /* parse_configuration.h */,
+ 40834C141FE05F3600C1BD0D /* parse_configuration.m */,
+ 40834C151FE05F3600C1BD0D /* Pinning */,
+ 40834C1B1FE05F3600C1BD0D /* Reporting */,
+ 40834C261FE05F3600C1BD0D /* TrustKit.h */,
+ 40834C271FE05F3600C1BD0D /* TrustKit.m */,
+ 40834C281FE05F3600C1BD0D /* TSKLog.h */,
+ 40834C291FE05F3600C1BD0D /* TSKPinningValidator.h */,
+ 40834C2A1FE05F3600C1BD0D /* TSKPinningValidator.m */,
+ 40834C2B1FE05F3600C1BD0D /* TSKPinningValidator_Private.h */,
+ 40834C2C1FE05F3600C1BD0D /* TSKPinningValidatorCallback.h */,
+ 40834C2D1FE05F3600C1BD0D /* TSKPinningValidatorResult.h */,
+ 40834C2E1FE05F3600C1BD0D /* TSKPinningValidatorResult.m */,
+ 40834C2F1FE05F3600C1BD0D /* TSKTrustDecision.h */,
+ 40834C301FE05F3600C1BD0D /* TSKTrustKitConfig.h */,
+ 40834C311FE05F3600C1BD0D /* TSKTrustKitConfig.m */,
+ );
+ name = TrustKit;
+ path = "vendor/mapbox-events-ios/vendor/TrustKit";
+ sourceTree = SOURCE_ROOT;
+ };
+ 40834C151FE05F3600C1BD0D /* Pinning */ = {
+ isa = PBXGroup;
+ children = (
+ 40834C161FE05F3600C1BD0D /* ssl_pin_verifier.h */,
+ 40834C171FE05F3600C1BD0D /* ssl_pin_verifier.m */,
+ 40834C181FE05F3600C1BD0D /* TSKPublicKeyAlgorithm.h */,
+ 40834C191FE05F3600C1BD0D /* TSKSPKIHashCache.h */,
+ 40834C1A1FE05F3600C1BD0D /* TSKSPKIHashCache.m */,
+ );
+ name = Pinning;
+ path = "vendor/mapbox-events-ios/vendor/TrustKit/Pinning";
+ sourceTree = SOURCE_ROOT;
+ };
+ 40834C1B1FE05F3600C1BD0D /* Reporting */ = {
+ isa = PBXGroup;
+ children = (
+ 40834C1C1FE05F3600C1BD0D /* reporting_utils.h */,
+ 40834C1D1FE05F3600C1BD0D /* reporting_utils.m */,
+ 40834C1E1FE05F3600C1BD0D /* TSKBackgroundReporter.h */,
+ 40834C1F1FE05F3600C1BD0D /* TSKBackgroundReporter.m */,
+ 40834C201FE05F3600C1BD0D /* TSKPinFailureReport.h */,
+ 40834C211FE05F3600C1BD0D /* TSKPinFailureReport.m */,
+ 40834C221FE05F3600C1BD0D /* TSKReportsRateLimiter.h */,
+ 40834C231FE05F3600C1BD0D /* TSKReportsRateLimiter.m */,
+ 40834C241FE05F3600C1BD0D /* vendor_identifier.h */,
+ 40834C251FE05F3600C1BD0D /* vendor_identifier.m */,
+ );
+ name = Reporting;
+ path = "vendor/mapbox-events-ios/vendor/TrustKit/Reporting";
+ sourceTree = SOURCE_ROOT;
+ };
409F43FB1E9E77D10048729D /* Swift Integration */ = {
isa = PBXGroup;
children = (
@@ -1242,6 +1666,7 @@
40CFA64E1D78754A008103BD /* Sources */ = {
isa = PBXGroup;
children = (
+ 07D8C6FD1F67562800381808 /* MGLComputedShapeSourceTests.m */,
071BBB051EE7761A001FB02A /* MGLImageSourceTests.m */,
40CFA6501D787579008103BD /* MGLShapeSourceTests.mm */,
920A3E5C1E6F995200C16EFC /* MGLSourceQueryTests.m */,
@@ -1272,6 +1697,8 @@
DABCABA91CB80692000A7C39 /* Benchmarking App */,
DA8847D31CBAF91600AB86E3 /* SDK */,
DA2E88521CC036F400F24E7B /* SDK Tests */,
+ 16376B301FFDB4B40000563E /* Integration Test Harness */,
+ 16376B081FFD9DAF0000563E /* Integration Tests */,
DA1DC9921CB6DF24006E619F /* Frameworks */,
DAC07C951CBB2CAD000CB309 /* Configuration */,
DA1DC94B1CB6C1C2006E619F /* Products */,
@@ -1288,6 +1715,8 @@
DA2E88511CC036F400F24E7B /* test.xctest */,
DA8933D51CCD306400E68420 /* Mapbox.bundle */,
DA25D5B91CCD9EDE00607828 /* Settings.bundle */,
+ 16376B071FFD9DAF0000563E /* integration.xctest */,
+ 16376B2F1FFDB4B40000563E /* Integration Test Harness.app */,
);
name = Products;
sourceTree = "<group>";
@@ -1369,22 +1798,24 @@
4031ACFD1E9FD26900A3EA26 /* Test Helpers */,
409F43FB1E9E77D10048729D /* Swift Integration */,
357579811D502AD4000B822E /* Styling */,
- DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */,
353D23951D0B0DFE002BE09D /* MGLAnnotationViewTests.m */,
+ DAEDC4331D603417000224FF /* MGLAttributionInfoTests.m */,
DA35A2C31CCA9F8300E826B2 /* MGLClockDirectionFormatterTests.m */,
35D9DDE11DA25EEC00DAAD69 /* MGLCodingTests.m */,
DA35A2C41CCA9F8300E826B2 /* MGLCompassDirectionFormatterTests.m */,
DA35A2A91CCA058D00E826B2 /* MGLCoordinateFormatterTests.m */,
+ 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */,
6407D66F1E0085FD00F6A9C3 /* MGLDocumentationExampleTests.swift */,
DA1F8F3C1EBD287B00367E42 /* MGLDocumentationGuideTests.swift */,
DD58A4C51D822BD000E1F038 /* MGLExpressionTests.mm */,
- 3598544C1E1D38AA00B29F84 /* MGLDistanceFormatterTests.m */,
DA0CD58F1CF56F6A00A5F5A5 /* MGLFeatureTests.mm */,
DA2E885C1CC0382C00F24E7B /* MGLGeometryTests.mm */,
DA5DB1291FABF1EE001C2326 /* MGLMapAccessibilityElementTests.m */,
16376B481FFEED010000563E /* MGLMapViewLayoutTests.m */,
+ 9658C154204761FC00D8A674 /* MGLMapViewScaleBarTests.m */,
35E208A61D24210F00EC9A46 /* MGLNSDataAdditionsTests.m */,
1F95931C1E6DE2E900D5B294 /* MGLNSDateAdditionsTests.mm */,
+ 96036A0520059BBA00510F3D /* MGLNSOrthographyAdditionsTests.m */,
DAE7DEC11E245455007505A6 /* MGLNSStringAdditionsTests.m */,
DA2E885D1CC0382C00F24E7B /* MGLOfflinePackTests.m */,
DA2E885E1CC0382C00F24E7B /* MGLOfflineRegionTests.m */,
@@ -1404,6 +1835,7 @@
isa = PBXGroup;
children = (
DA88485E1CBAFC2E00AB86E3 /* Mapbox.h */,
+ 40834AEF1FDF4F0100C1BD0D /* Mapbox-Prefix.pch */,
DA8847DE1CBAFA3E00AB86E3 /* Foundation */,
DA8F25BC1D51D2570010E6B5 /* Foundation Templates */,
DA8933B91CCD2C6700E68420 /* Foundation Resources */,
@@ -1425,7 +1857,6 @@
DAD165801CF4CF9A001FF4B9 /* Formatters */,
DAD165811CF4CFC4001FF4B9 /* Geometry */,
DAD165821CF4CFE3001FF4B9 /* Offline Maps */,
- DA8848911CBB049300AB86E3 /* reachability */,
DA8847DF1CBAFA5100AB86E3 /* MGLAccountManager.h */,
DA8847FF1CBAFA6200AB86E3 /* MGLAccountManager_Private.h */,
DA8848001CBAFA6200AB86E3 /* MGLAccountManager.m */,
@@ -1484,13 +1915,6 @@
DA89339F1CCC951200E68420 /* Localizable.strings */,
DAC49C5F1CD02BC9009E1AA3 /* Localizable.stringsdict */,
DA8933EF1CCD387900E68420 /* strip-frameworks.sh */,
- 40599F001DEE1B2400182B5D /* api_mapbox_staging.der */,
- AC0C15F2209D0E6000B65675 /* api_mapbox_cn-digicert_2018.der */,
- AC0C15F1209D0E3600B65675 /* api_mapbox_cn-geotrust_2018.der */,
- 40599F011DEE1B2400182B5D /* api_mapbox_com-digicert_2016.der */,
- 40599F021DEE1B2400182B5D /* api_mapbox_com-geotrust_2016.der */,
- 40EA6BBD1EF4598900FCCDA2 /* api_mapbox_com-digicert_2017.der */,
- 40EA6BBE1EF4598900FCCDA2 /* api_mapbox_com-geotrust_2017.der */,
);
name = "Kit Resources";
path = resources;
@@ -1518,16 +1942,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 = (
@@ -1632,8 +2046,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 */,
@@ -1664,10 +2080,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 */,
@@ -1684,6 +2096,8 @@
408AA8561DAEDA0800022900 /* NSDictionary+MGLAdditions.mm */,
DA8848141CBAFA6200AB86E3 /* NSException+MGLAdditions.h */,
3510FFEE1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h */,
+ DA9EA82A201C0C0B00F9874D /* NSExpression+MGLAdditions.h */,
+ DAC25FCB200FD83E009BE98E /* NSExpression+MGLPrivateAdditions.h */,
3510FFEF1D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm */,
35B82BF61D6C5F8400B1B721 /* NSPredicate+MGLAdditions.h */,
35B82BF71D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm */,
@@ -1734,16 +2148,10 @@
DAD165851CF4D08B001FF4B9 /* Telemetry */ = {
isa = PBXGroup;
children = (
- DA8848421CBAFB9800AB86E3 /* MGLAPIClient.h */,
- DA8848431CBAFB9800AB86E3 /* MGLAPIClient.m */,
- DA8848461CBAFB9800AB86E3 /* MGLLocationManager.h */,
- DA8848471CBAFB9800AB86E3 /* MGLLocationManager.m */,
- DA8848481CBAFB9800AB86E3 /* MGLMapboxEvents.h */,
- DA8848491CBAFB9800AB86E3 /* MGLMapboxEvents.m */,
- 9620BB361E69FE1700705A1D /* MGLSDKUpdateChecker.h */,
- 9620BB371E69FE1700705A1D /* MGLSDKUpdateChecker.mm */,
AC518DFD201BB55A00EBC820 /* MGLTelemetryConfig.h */,
AC518DFE201BB55A00EBC820 /* MGLTelemetryConfig.m */,
+ 40834BA21FE05D3100C1BD0D /* Runtime */,
+ 40834BA11FE05CFD00C1BD0D /* Development */,
);
name = Telemetry;
sourceTree = "<group>";
@@ -1758,6 +2166,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 */,
@@ -1769,15 +2178,14 @@
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 /* MGLComputedShapeSource_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 */,
404326891D5B9B27007111BD /* MGLAnnotationContainerView_Private.h in Headers */,
CA55CD41202C16AA00CE7095 /* MGLCameraChangeReason.h in Headers */,
- 1FB7DAAF1F2A4DBD00410606 /* MGLVectorSource+MGLAdditions.h in Headers */,
DA88483B1CBAFB8500AB86E3 /* MGLCalloutView.h in Headers */,
35E0CFE61D3E501500188327 /* MGLStyle_Private.h in Headers */,
3510FFF01D6D9D8C00F413B2 /* NSExpression+MGLAdditions.h in Headers */,
@@ -1790,20 +2198,19 @@
DA27C24F1CBB4C11000B0ECD /* MGLAccountManager_Private.h in Headers */,
DA8847FC1CBAFA5100AB86E3 /* MGLStyle.h in Headers */,
DD9BE4F71EB263C50079A3AF /* UIViewController+MGLAdditions.h in Headers */,
- DAF0D8131DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
+ DAF0D8131DFE0EC500B28378 /* MGLVectorTileSource_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 */,
DA8847EF1CBAFA5100AB86E3 /* MGLAccountManager.h in Headers */,
- DA8848511CBAFB9800AB86E3 /* MGLAPIClient.h in Headers */,
DA35A2C91CCAAAD200E826B2 /* NSValue+MGLAdditions.h in Headers */,
3510FFEA1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.h in Headers */,
DA6408DB1DA4E7D300908C90 /* MGLVectorStyleLayer.h in Headers */,
@@ -1811,14 +2218,15 @@
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 */,
071BBB031EE76146001FB02A /* MGLImageSource.h in Headers */,
DA8847F41CBAFA5100AB86E3 /* MGLOfflinePack.h in Headers */,
DA88482E1CBAFA6200AB86E3 /* NSException+MGLAdditions.h in Headers */,
- DA8848551CBAFB9800AB86E3 /* MGLLocationManager.h in Headers */,
96F3F73C1F57124B003E2D2C /* MGLUserLocationHeadingIndicator.h in Headers */,
408AA8571DAEDA1700022900 /* NSDictionary+MGLAdditions.h in Headers */,
DA88483F1CBAFB8500AB86E3 /* MGLUserLocation.h in Headers */,
@@ -1843,13 +2251,15 @@
DA8847F11CBAFA5100AB86E3 /* MGLGeometry.h in Headers */,
DA8848221CBAFA6200AB86E3 /* MGLOfflineRegion_Private.h in Headers */,
35136D4C1D4277FC00C20EFD /* MGLSource.h in Headers */,
- 3566C76C1D4A8DFA008152BC /* MGLRasterSource.h in Headers */,
+ 3566C76C1D4A8DFA008152BC /* MGLRasterTileSource.h in Headers */,
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 */,
@@ -1865,6 +2275,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 */,
@@ -1875,9 +2286,10 @@
DA8848841CBB033F00AB86E3 /* FABAttributes.h in Headers */,
DA8847FD1CBAFA5100AB86E3 /* MGLTilePyramidOfflineRegion.h in Headers */,
DA88482F1CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.h in Headers */,
+ DA9EA82B201C0C0C00F9874D /* NSExpression+MGLAdditions.h in Headers */,
DA8848601CBAFC2E00AB86E3 /* Mapbox.h in Headers */,
- DAF0D8101DFE0EA000B28378 /* MGLRasterSource_Private.h in Headers */,
- 350098BB1D480108004B2AF0 /* MGLVectorSource.h in Headers */,
+ DAF0D8101DFE0EA000B28378 /* MGLRasterTileSource_Private.h in Headers */,
+ 350098BB1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */,
DA8847F61CBAFA5100AB86E3 /* MGLOfflineStorage.h in Headers */,
DAD1656E1CF41981001FF4B9 /* MGLFeature_Private.h in Headers */,
DA88483C1CBAFB8500AB86E3 /* MGLMapView.h in Headers */,
@@ -1890,24 +2302,34 @@
buildActionMask = 2147483647;
files = (
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 */,
+ 350098BC1D480108004B2AF0 /* MGLVectorTileSource.h in Headers */,
FA68F14B1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.h in Headers */,
+ 96E516DE200054F700A02306 /* MGLGeometry_Private.h in Headers */,
353933FC1D3FB7C0003F57D7 /* MGLRasterStyleLayer.h in Headers */,
- 3566C76D1D4A8DFA008152BC /* MGLRasterSource.h in Headers */,
+ 3566C76D1D4A8DFA008152BC /* MGLRasterTileSource.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 */,
+ DA9EA82C201C0C0C00F9874D /* NSExpression+MGLAdditions.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 */,
@@ -1917,22 +2339,38 @@
353933FF1D3FB7DD003F57D7 /* MGLSymbolStyleLayer.h in Headers */,
DAAF722E1DA903C700312FA4 /* MGLStyleValue_Private.h in Headers */,
DABFB8661CBE99E500D62B32 /* MGLPointAnnotation.h in Headers */,
+ 96E5170220005A6600A02306 /* FABAttributes.h in Headers */,
+ 96E516E42000560B00A02306 /* MGLComputedShapeSource_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 */,
+ 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 */,
@@ -1941,49 +2379,101 @@
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 */,
40F887711D7A1E59008ECB67 /* MGLShapeSource_Private.h in Headers */,
DABFB8631CBE99E500D62B32 /* MGLOfflineRegion.h in Headers */,
DA35A2B21CCA141D00E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
- DAF0D8141DFE0EC500B28378 /* MGLVectorSource_Private.h in Headers */,
+ DAF0D8141DFE0EC500B28378 /* MGLVectorTileSource_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 */,
+ DAF0D8111DFE0EA000B28378 /* MGLRasterTileSource_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 */,
+ 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 */,
+ CABE5DAC2072FA660003AF3C /* 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 */,
+ CAA69DA6206DCD0E007279CD /* Embed Frameworks */,
+ );
+ 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" */;
@@ -2116,9 +2606,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;
@@ -2171,6 +2670,9 @@
hu,
bg,
ar,
+ he,
+ da,
+ "pt-PT",
);
mainGroup = DA1DC9411CB6C1C2006E619F;
productRefGroup = DA1DC94B1CB6C1C2006E619F /* Products */;
@@ -2184,11 +2686,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;
@@ -2233,17 +2754,10 @@
files = (
DA8933BC1CCD2CA100E68420 /* Foundation.strings in Resources */,
DA8933A31CCC95B000E68420 /* Localizable.strings in Resources */,
- AC0C15F5209D0E7200B65675 /* api_mapbox_cn-digicert_2018.der in Resources */,
960D0C361ECF5AAF008E151F /* Images.xcassets in Resources */,
DA8933F01CCD387900E68420 /* strip-frameworks.sh in Resources */,
DAC49C5C1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
DA8933BF1CCD2CAD00E68420 /* Foundation.stringsdict in Resources */,
- 40EA6BC11EF4599600FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */,
- 408982E91DEE208200754016 /* api_mapbox_staging.der in Resources */,
- AC0C15F3209D0E6900B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */,
- 408982EA1DEE208B00754016 /* api_mapbox_com-digicert_2016.der in Resources */,
- 40EA6BC31EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */,
- 408982EB1DEE209100754016 /* api_mapbox_com-geotrust_2016.der in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2251,18 +2765,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- AC0C15F4209D0E7000B65675 /* api_mapbox_cn-geotrust_2018.der in Resources */,
DA8933E01CCD31DF00E68420 /* Localizable.strings in Resources */,
DA8933DB1CCD31D400E68420 /* Foundation.strings in Resources */,
960D0C371ECF5AAF008E151F /* Images.xcassets in Resources */,
DA8933DC1CCD31D400E68420 /* Foundation.stringsdict in Resources */,
- 40EA6BC41EF4599D00FCCDA2 /* api_mapbox_com-geotrust_2017.der in Resources */,
DAC49C5D1CD02BC9009E1AA3 /* Localizable.stringsdict in Resources */,
- 40599F0C1DEE1B7600182B5D /* api_mapbox_staging.der in Resources */,
- 40599F0D1DEE1B7A00182B5D /* api_mapbox_com-digicert_2016.der in Resources */,
- 40599F0E1DEE1B7E00182B5D /* api_mapbox_com-geotrust_2016.der in Resources */,
- AC0C15F6209D0E7300B65675 /* api_mapbox_cn-digicert_2018.der in Resources */,
- 40EA6BC21EF4599700FCCDA2 /* api_mapbox_com-digicert_2017.der in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2282,6 +2789,24 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
+ 16376B031FFD9DAF0000563E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 16376B0A1FFD9DAF0000563E /* MBGLIntegrationTests.m in Sources */,
+ CA0C27942076CA19001CE5B7 /* MGLMapViewIntegrationTest.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;
@@ -2304,10 +2829,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 */,
@@ -2322,21 +2848,24 @@
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 */,
+ 9658C155204761FC00D8A674 /* MGLMapViewScaleBarTests.m 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 */,
@@ -2352,78 +2881,114 @@
files = (
35136D391D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFEC1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D801D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
+ 40834C431FE05F7500C1BD0D /* TSKSPKIHashCache.m in Sources */,
DAED38651D62D0FC00D7640F /* NSURL+MGLAdditions.m in Sources */,
+ 40834BEC1FE05E1800C1BD0D /* MMEEvent.m in Sources */,
9620BB3A1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
354B83981D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
+ 40834BEE1FE05E1800C1BD0D /* MMEEventsConfiguration.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 */,
+ 40834C481FE05F7500C1BD0D /* vendor_identifier.m in Sources */,
DA8848541CBAFB9800AB86E3 /* MGLCompactCalloutView.m in Sources */,
+ 40834BEB1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */,
+ 40834C411FE05F7500C1BD0D /* parse_configuration.m in Sources */,
DA8848251CBAFA6200AB86E3 /* MGLPointAnnotation.mm in Sources */,
+ 40834BEA1FE05E1800C1BD0D /* MMEConstants.m in Sources */,
929EFFAB1F56DCD4003A77D5 /* MGLAnnotationView.mm in Sources */,
35136D3C1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */,
DD9BE4F81EB263C50079A3AF /* UIViewController+MGLAdditions.m in Sources */,
350098DE1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
DA6408DD1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
+ 40834BF71FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */,
3566C7681D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
+ 40834C4A1FE05F7500C1BD0D /* TSKPinningValidator.m in Sources */,
400533021DB0862B0069F638 /* NSArray+MGLAdditions.mm in Sources */,
+ 96036A03200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
+ 40834BF31FE05E1800C1BD0D /* MMETimerManager.m in Sources */,
35136D421D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
3538AA1F1D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
+ 40834BF11FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */,
+ 40834C461FE05F7500C1BD0D /* TSKPinFailureReport.m in Sources */,
+ 406E99B91FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */,
+ 40834BE61FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */,
DA00FC901D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
DA88482D1CBAFA6200AB86E3 /* NSBundle+MGLAdditions.m in Sources */,
+ 406E99BB1FFF006C00D9FFCC /* MMEUINavigation.m in Sources */,
966FCF541F3C323300F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
+ 40834BE81FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */,
DA88485B1CBAFB9800AB86E3 /* MGLUserLocation.m in Sources */,
927FBD011F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
- 350098BD1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
- 3566C76E1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
+ 350098BD1D480108004B2AF0 /* MGLVectorTileSource.mm in Sources */,
+ 3566C76E1D4A8DFA008152BC /* MGLRasterTileSource.mm in Sources */,
DA88488C1CBB037E00AB86E3 /* SMCalloutView.m in Sources */,
35136D4E1D4277FC00C20EFD /* MGLSource.mm in Sources */,
1F06668D1EC64F8E001C16D7 /* MGLLight.mm in Sources */,
DA35A2B81CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
DAD1657A1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */,
+ DAF25719201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */,
+ 40834BF51FE05E1800C1BD0D /* MMETypes.m in Sources */,
35136D451D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */,
+ 40834C491FE05F7500C1BD0D /* TrustKit.m in Sources */,
35599DED1D46F14E0048254D /* MGLStyleValue.mm in Sources */,
DA8848211CBAFA6200AB86E3 /* MGLOfflinePack.mm in Sources */,
+ 0778DD441F67556C00A73B34 /* MGLComputedShapeSource.mm in Sources */,
3557F7B21E1D27D300CCA5E6 /* MGLDistanceFormatter.m in Sources */,
+ 40834C4B1FE05F7500C1BD0D /* TSKPinningValidatorResult.m in Sources */,
+ 40834BE71FE05E1800C1BD0D /* MMEAPIClient.m in Sources */,
DA8848591CBAFB9800AB86E3 /* MGLMapView.mm in Sources */,
DA8848501CBAFB9800AB86E3 /* MGLAnnotationImage.m in Sources */,
+ 40834BF01FE05E1800C1BD0D /* MMELocationManager.m in Sources */,
DA8848281CBAFA6200AB86E3 /* MGLShape.mm in Sources */,
DA35A2B31CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
DD0902A91DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */,
35D13AB91D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */,
+ 40834C4C1FE05F7500C1BD0D /* TSKTrustKitConfig.m in Sources */,
DA35A2CB1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
071BBB001EE7613F001FB02A /* MGLImageSource.mm in Sources */,
DA8848321CBAFA6200AB86E3 /* NSString+MGLAdditions.m in Sources */,
+ 40834C441FE05F7500C1BD0D /* reporting_utils.m in Sources */,
408AA8581DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
DA35A2A11CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
35305D481D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */,
+ 40834BF61FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */,
DA8848291CBAFA6200AB86E3 /* MGLStyle.mm in Sources */,
357FE2DF1E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */,
+ 40834BF81FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */,
DA88481C1CBAFA6200AB86E3 /* MGLGeometry.mm in Sources */,
558DE7A21E5615E400C7916D /* MGLFoundation.mm in Sources */,
+ 40834BE91FE05E1800C1BD0D /* MMECommonEventData.m in Sources */,
3510FFF21D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */,
DA88481F1CBAFA6200AB86E3 /* MGLMultiPoint.mm in Sources */,
DA88482B1CBAFA6200AB86E3 /* MGLTypes.m in Sources */,
FA68F14D1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
404C26E41D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0011E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
+ 40834C451FE05F7500C1BD0D /* TSKBackgroundReporter.m 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 */,
+ 40834C401FE05F7500C1BD0D /* configuration_utils.m in Sources */,
+ 40834BF91FE05E1800C1BD0D /* MMEReachability.m in Sources */,
+ 40834BF21FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */,
+ 40834C471FE05F7500C1BD0D /* TSKReportsRateLimiter.m in Sources */,
966FCF4E1F3A5C9200F2B6DE /* MGLUserLocationHeadingBeamLayer.m in Sources */,
+ 8989B17E201A48EB0081CF59 /* MGLHeatmapStyleLayer.mm in Sources */,
DA8848301CBAFA6200AB86E3 /* NSProcessInfo+MGLAdditions.m in Sources */,
+ 40834BED1FE05E1800C1BD0D /* MMEEventLogger.m in Sources */,
353AFA161D65AB17005A69F4 /* NSDate+MGLAdditions.mm in Sources */,
+ 40834BF41FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */,
+ 40834BEF1FE05E1800C1BD0D /* MMEEventsManager.m in Sources */,
35D13AC51D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
DA8848241CBAFA6200AB86E3 /* MGLOfflineStorage.mm in Sources */,
DA88482A1CBAFA6200AB86E3 /* MGLTilePyramidOfflineRegion.mm in Sources */,
4049C29F1DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D3F1D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC41F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
+ 40834C421FE05F7500C1BD0D /* ssl_pin_verifier.m in Sources */,
DA72620D1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DA88481A1CBAFA6200AB86E3 /* MGLAccountManager.m in Sources */,
3510FFFB1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2431,7 +2996,6 @@
DA8848271CBAFA6200AB86E3 /* MGLPolyline.mm in Sources */,
DA8848581CBAFB9800AB86E3 /* MGLMapboxEvents.m in Sources */,
35CE61841D4165D9004F2359 /* UIColor+MGLAdditions.mm in Sources */,
- DA8848561CBAFB9800AB86E3 /* MGLLocationManager.m in Sources */,
3EA93369F61CF70AFA50465D /* MGLRendererConfiguration.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -2442,78 +3006,113 @@
files = (
35136D3A1D42271A00C20EFD /* MGLBackgroundStyleLayer.mm in Sources */,
3510FFED1D6D9C7A00F413B2 /* NSComparisonPredicate+MGLAdditions.mm in Sources */,
- 7E016D811D9E86BE00A29A21 /* MGLPolyline+MGLAdditions.m in Sources */,
+ 40834C501FE05F7600C1BD0D /* TSKSPKIHashCache.m in Sources */,
354B83991D2E873E005D9406 /* MGLUserLocationAnnotationView.m in Sources */,
+ 40834C001FE05E1800C1BD0D /* MMEEvent.m in Sources */,
9620BB3B1E69FE1700705A1D /* MGLSDKUpdateChecker.mm in Sources */,
DAA4E4221CBB730400178DFB /* MGLPointAnnotation.mm in Sources */,
+ 40834C021FE05E1800C1BD0D /* MMEEventsConfiguration.m 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 */,
+ 40834C551FE05F7600C1BD0D /* vendor_identifier.m in Sources */,
DAA4E4291CBB730400178DFB /* NSBundle+MGLAdditions.m in Sources */,
- DAA4E42E1CBB730400178DFB /* MGLAPIClient.m in Sources */,
+ 40834BFF1FE05E1800C1BD0D /* MMEDependencyManager.m in Sources */,
+ 40834C4E1FE05F7600C1BD0D /* parse_configuration.m in Sources */,
+ 40834BFE1FE05E1800C1BD0D /* MMEConstants.m in Sources */,
35136D3D1D42272500C20EFD /* MGLCircleStyleLayer.mm in Sources */,
DD9BE4FA1EB263F40079A3AF /* UIViewController+MGLAdditions.m in Sources */,
350098DF1D484E60004B2AF0 /* NSValue+MGLStyleAttributeAdditions.mm in Sources */,
DA6408DE1DA4E7D300908C90 /* MGLVectorStyleLayer.m in Sources */,
3566C7691D4A77BA008152BC /* MGLShapeSource.mm in Sources */,
+ 40834C0B1FE05E1800C1BD0D /* MMEUniqueIdentifier.m in Sources */,
400533031DB086490069F638 /* NSArray+MGLAdditions.mm in Sources */,
+ 40834C571FE05F7600C1BD0D /* TSKPinningValidator.m in Sources */,
35136D431D42274500C20EFD /* MGLRasterStyleLayer.mm in Sources */,
+ 96036A04200565C700510F3D /* NSOrthography+MGLAdditions.m in Sources */,
+ 40834C071FE05E1800C1BD0D /* MMETimerManager.m in Sources */,
3538AA201D542239008EC33D /* MGLForegroundStyleLayer.mm in Sources */,
DA00FC911D5EEB0D009AABC8 /* MGLAttributionInfo.mm in Sources */,
+ 40834C051FE05E1800C1BD0D /* MMENSDateWrapper.m in Sources */,
+ 40834C531FE05F7600C1BD0D /* TSKPinFailureReport.m in Sources */,
+ 40834BFA1FE05E1800C1BD0D /* CLLocation+MMEMobileEvents.m in Sources */,
+ 406E99BA1FFEFF1B00D9FFCC /* MMEEventLogReportViewController.m in Sources */,
DAA4E4201CBB730400178DFB /* MGLOfflinePack.mm in Sources */,
966FCF551F3C323500F2B6DE /* MGLUserLocationHeadingArrowLayer.m in Sources */,
DAA4E4331CBB730400178DFB /* MGLUserLocation.m in Sources */,
+ 406E99BC1FFF006D00D9FFCC /* MMEUINavigation.m in Sources */,
+ 40834BFC1FE05E1800C1BD0D /* MMECategoryLoader.m in Sources */,
927FBD021F4DB05500F8BF1F /* MGLMapSnapshotter.mm in Sources */,
- 350098BE1D480108004B2AF0 /* MGLVectorSource.mm in Sources */,
- 3566C76F1D4A8DFA008152BC /* MGLRasterSource.mm in Sources */,
+ 350098BE1D480108004B2AF0 /* MGLVectorTileSource.mm in Sources */,
+ 3566C76F1D4A8DFA008152BC /* MGLRasterTileSource.mm in Sources */,
DAA4E4351CBB730400178DFB /* SMCalloutView.m in Sources */,
35136D4F1D4277FC00C20EFD /* MGLSource.mm in Sources */,
DA35A2B91CCA9A5D00E826B2 /* MGLClockDirectionFormatter.m in Sources */,
DAD1657B1CF4CDFF001FF4B9 /* MGLShapeCollection.mm in Sources */,
DAA4E4251CBB730400178DFB /* MGLShape.mm in Sources */,
35136D461D42275100C20EFD /* MGLSymbolStyleLayer.mm in Sources */,
+ DAF2571A201901E200367EF5 /* MGLHillshadeStyleLayer.mm in Sources */,
+ 40834C091FE05E1800C1BD0D /* MMETypes.m in Sources */,
35599DEE1D46F14E0048254D /* MGLStyleValue.mm in Sources */,
+ 40834C561FE05F7600C1BD0D /* TrustKit.m in Sources */,
DAA4E42B1CBB730400178DFB /* NSString+MGLAdditions.m in Sources */,
DAA4E4261CBB730400178DFB /* MGLStyle.mm in Sources */,
DAA32CC31E4C6B65006F8D24 /* MGLDistanceFormatter.m in Sources */,
DAA4E41D1CBB730400178DFB /* MGLGeometry.mm in Sources */,
+ 40834C581FE05F7600C1BD0D /* TSKPinningValidatorResult.m in Sources */,
+ 40834BFB1FE05E1800C1BD0D /* MMEAPIClient.m in Sources */,
DAA4E41F1CBB730400178DFB /* MGLMultiPoint.mm in Sources */,
DD0902AA1DB1929D00C5BDCE /* MGLNetworkConfiguration.m in Sources */,
+ 40834C041FE05E1800C1BD0D /* MMELocationManager.m in Sources */,
DA35A2B41CCA141D00E826B2 /* MGLCompassDirectionFormatter.m in Sources */,
35D13ABA1D3D15E300AFB4E0 /* MGLStyleLayer.mm in Sources */,
071BBAFF1EE7613E001FB02A /* MGLImageSource.mm in Sources */,
DA35A2CC1CCAAAD200E826B2 /* NSValue+MGLAdditions.m in Sources */,
+ 40834C591FE05F7600C1BD0D /* TSKTrustKitConfig.m in Sources */,
408AA8591DAEDA1E00022900 /* NSDictionary+MGLAdditions.mm in Sources */,
DAA4E4281CBB730400178DFB /* MGLTypes.m in Sources */,
DA35A2A21CC9E95F00E826B2 /* MGLCoordinateFormatter.m in Sources */,
+ 40834C511FE05F7600C1BD0D /* reporting_utils.m in Sources */,
35305D491D22AA680007D005 /* NSData+MGLAdditions.mm in Sources */,
357FE2E01E02D2B20068B753 /* NSCoder+MGLAdditions.mm in Sources */,
DAA4E42D1CBB730400178DFB /* MGLAnnotationImage.m in Sources */,
+ 40834C0A1FE05E1800C1BD0D /* MMEUIApplicationWrapper.m in Sources */,
558DE7A31E5615E400C7916D /* MGLFoundation.mm in Sources */,
3510FFF31D6D9D8C00F413B2 /* NSExpression+MGLAdditions.mm in Sources */,
- DAA4E4301CBB730400178DFB /* MGLLocationManager.m in Sources */,
+ 40834C0C1FE05E1800C1BD0D /* NSData+MMEGZIP.m in Sources */,
DAA4E4321CBB730400178DFB /* MGLMapView.mm in Sources */,
+ 40834BFD1FE05E1800C1BD0D /* MMECommonEventData.m in Sources */,
DAA4E41E1CBB730400178DFB /* MGLMapCamera.mm in Sources */,
FA68F14E1E9D656600F9F6C2 /* MGLFillExtrusionStyleLayer.mm in Sources */,
1F7454921ECBB42C00021D39 /* MGLLight.mm in Sources */,
404C26E51D89B877000AA13D /* MGLTileSource.mm in Sources */,
355AE0021E9281DA00F3939D /* MGLScaleBar.mm in Sources */,
4018B1C81CDC287F00F666AF /* MGLAnnotationView.mm in Sources */,
+ 07D8C6FB1F67560100381808 /* MGLComputedShapeSource.mm in Sources */,
+ 40834C521FE05F7600C1BD0D /* TSKBackgroundReporter.m in Sources */,
DAA4E4341CBB730400178DFB /* MGLFaux3DUserLocationAnnotationView.m in Sources */,
+ DACA86292019218600E9693A /* MGLRasterDEMSource.mm in Sources */,
35B82BFB1D6C5F8400B1B721 /* NSPredicate+MGLAdditions.mm in Sources */,
- 7E016D871D9E890300A29A21 /* MGLPolygon+MGLAdditions.m in Sources */,
+ 40834C4D1FE05F7600C1BD0D /* configuration_utils.m in Sources */,
+ 40834C0D1FE05E1800C1BD0D /* MMEReachability.m in Sources */,
+ 40834C061FE05E1800C1BD0D /* MMENSURLSessionWrapper.m in Sources */,
+ 40834C541FE05F7600C1BD0D /* TSKReportsRateLimiter.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 */,
+ 40834C011FE05E1800C1BD0D /* MMEEventLogger.m in Sources */,
35D13AC61D3D19DD00AFB4E0 /* MGLFillStyleLayer.mm in Sources */,
+ 40834C081FE05E1800C1BD0D /* MMETrustKitWrapper.m in Sources */,
+ 40834C031FE05E1800C1BD0D /* MMEEventsManager.m in Sources */,
DAA4E42A1CBB730400178DFB /* NSProcessInfo+MGLAdditions.m in Sources */,
DAA4E4211CBB730400178DFB /* MGLOfflineStorage.mm in Sources */,
4049C2A01DB6CD6C00B3F799 /* MGLPointCollection.mm in Sources */,
35136D401D42273000C20EFD /* MGLLineStyleLayer.mm in Sources */,
DA704CC51F65A475004B3F28 /* MGLMapAccessibilityElement.mm in Sources */,
+ 40834C4F1FE05F7600C1BD0D /* ssl_pin_verifier.m in Sources */,
DA72620E1DEEE3480043BB89 /* MGLOpenGLStyleLayer.mm in Sources */,
DAA4E42F1CBB730400178DFB /* MGLCompactCalloutView.m in Sources */,
3510FFFC1D6DCC4700F413B2 /* NSCompoundPredicate+MGLAdditions.mm in Sources */,
@@ -2540,6 +3139,16 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
+ 165D0CE620005351009A3C66 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = DA8847D11CBAF91600AB86E3 /* dynamic */;
+ targetProxy = 165D0CE520005351009A3C66 /* PBXContainerItemProxy */;
+ };
+ CABE5DAC2072FA660003AF3C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 16376B2E1FFDB4B40000563E /* Integration Test Harness */;
+ targetProxy = CABE5DAB2072FA660003AF3C /* PBXContainerItemProxy */;
+ };
DA25D5C81CCDA0C100607828 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = DA25D5B81CCD9EDE00607828 /* settings */;
@@ -2573,6 +3182,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 = (
@@ -2596,6 +3213,9 @@
DA5C09BA1EFC48550056B178 /* hu */,
DA3389651FA3EE1B001EA329 /* bg */,
DA80E9601FE84AD90065FC9B /* ar */,
+ DACBC60B20118ABE00C4D7E2 /* he */,
+ DAD88E07202ACFE800AAA536 /* da */,
+ DA93409B208562EB0059919A /* pt-PT */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2621,6 +3241,9 @@
DAE8CCAE1E6E8C76009B5CB0 /* nl */,
DACCD9C81F1F473700BB09A1 /* hu */,
DA33896A1FA3EE58001EA329 /* bg */,
+ DACBC60E20118AFE00C4D7E2 /* he */,
+ DAD88E0C202AD06500AAA536 /* da */,
+ DA93409F208563440059919A /* pt-PT */,
);
name = Root.strings;
sourceTree = "<group>";
@@ -2644,6 +3267,9 @@
DA618B1B1E68884E00CB7F44 /* ca */,
DA5C09BB1EFC486C0056B178 /* hu */,
DA3389681FA3EE48001EA329 /* bg */,
+ DACBC60D20118ADE00C4D7E2 /* he */,
+ DAD88E0A202AD03C00AAA536 /* da */,
+ DA93409D208563220059919A /* pt-PT */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -2665,6 +3291,8 @@
DA704CC71F6663A3004B3F28 /* uk */,
DA33895F1FA3EAB7001EA329 /* pt-BR */,
DA3389661FA3EE28001EA329 /* bg */,
+ DACBC60C20118AD000C4D7E2 /* he */,
+ DAD88E08202AD01300AAA536 /* da */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -2687,6 +3315,8 @@
DA3389671FA3EE2F001EA329 /* bg */,
DA33896B1FA3EF4A001EA329 /* hu */,
DA80E9611FE84AEF0065FC9B /* ar */,
+ DAD88E09202AD01F00AAA536 /* da */,
+ DA93409C2085630C0059919A /* pt-PT */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -2715,6 +3345,8 @@
DA704CBC1F637405004B3F28 /* uk */,
DA704CBD1F63746E004B3F28 /* zh-Hant */,
DA3389691FA3EE50001EA329 /* bg */,
+ DAD88E0B202AD04D00AAA536 /* da */,
+ DA93409E208563360059919A /* pt-PT */,
);
name = Localizable.stringsdict;
sourceTree = "<group>";
@@ -2722,6 +3354,104 @@
/* 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";
+ 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";
+ 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";
+ 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";
+ 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 = {
@@ -2732,7 +3462,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;
@@ -2740,7 +3472,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;
@@ -2766,7 +3502,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -2785,7 +3521,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;
@@ -2793,7 +3531,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;
@@ -2813,9 +3555,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = ../../build/ios;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@@ -2828,7 +3571,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = "Mapbox GL";
@@ -2842,7 +3584,6 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = "$(SRCROOT)/app/Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = "Mapbox GL";
@@ -2877,8 +3618,8 @@
CLANG_ENABLE_MODULES = YES;
HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
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)",
@@ -2901,8 +3642,8 @@
CLANG_ENABLE_MODULES = YES;
HEADER_SEARCH_PATHS = "$(mbgl_core_INCLUDE_DIRECTORIES)";
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)",
@@ -2922,18 +3663,19 @@
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;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
+ GCC_PREFIX_HEADER = "$SRCROOT/src/Mapbox-Prefix.pch";
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
"$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = framework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
@@ -2963,18 +3705,19 @@
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;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
+ GCC_PREFIX_HEADER = "$SRCROOT/src/Mapbox-Prefix.pch";
HEADER_SEARCH_PATHS = (
"$(mbgl_core_INCLUDE_DIRECTORIES)",
"$(mbgl_filesource_INCLUDE_DIRECTORIES)",
);
INFOPLIST_FILE = framework/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
OTHER_CFLAGS = "-fvisibility=hidden";
OTHER_CPLUSPLUSFLAGS = (
@@ -3088,7 +3831,6 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench;
PRODUCT_NAME = "Bench GL";
@@ -3100,7 +3842,6 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = "$(SRCROOT)/benchmark/Info.plist";
- IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.bench;
PRODUCT_NAME = "Bench GL";
@@ -3110,6 +3851,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..34dcc1f50b 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' />
@@ -17,11 +17,12 @@ framework_root: ../darwin/src
custom_categories:
- name: Guides
children:
- - Adding Points to a Map
+ - Adding Markers to a Map
- Runtime Styling
- - Using Style Functions at Runtime
+ - Migrating to Expressions
- 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:
@@ -77,11 +77,13 @@ custom_categories:
- name: Style Content
children:
- MGLSource
+ - MGLShapeSource
+ - MGLComputedShapeSource
- MGLTileSource
+ - MGLRasterTileSource
+ - MGLRasterDEMSource
+ - MGLVectorTileSource
- MGLImageSource
- - MGLShapeSource
- - MGLRasterSource
- - MGLVectorSource
- name: Style Layers
children:
- MGLStyleLayer
@@ -92,6 +94,8 @@ custom_categories:
- MGLCircleStyleLayer
- MGLFillStyleLayer
- MGLFillExtrusionStyleLayer
+ - MGLHeatmapStyleLayer
+ - MGLHillshadeStyleLayer
- MGLLineStyleLayer
- MGLSymbolStyleLayer
- name: Offline Maps
diff --git a/platform/ios/resources/api_mapbox_cn-digicert_2018.der b/platform/ios/resources/api_mapbox_cn-digicert_2018.der
deleted file mode 100644
index e458713337..0000000000
--- a/platform/ios/resources/api_mapbox_cn-digicert_2018.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_cn-geotrust_2018.der b/platform/ios/resources/api_mapbox_cn-geotrust_2018.der
deleted file mode 100644
index e3d4b222ae..0000000000
--- a/platform/ios/resources/api_mapbox_cn-geotrust_2018.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-digicert_2016.der b/platform/ios/resources/api_mapbox_com-digicert_2016.der
deleted file mode 100644
index e8ef427f33..0000000000
--- a/platform/ios/resources/api_mapbox_com-digicert_2016.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-digicert_2017.der b/platform/ios/resources/api_mapbox_com-digicert_2017.der
deleted file mode 100644
index 4a190085ab..0000000000
--- a/platform/ios/resources/api_mapbox_com-digicert_2017.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-geotrust_2016.der b/platform/ios/resources/api_mapbox_com-geotrust_2016.der
deleted file mode 100644
index 1c7331dedc..0000000000
--- a/platform/ios/resources/api_mapbox_com-geotrust_2016.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_com-geotrust_2017.der b/platform/ios/resources/api_mapbox_com-geotrust_2017.der
deleted file mode 100644
index 7bb9befbbf..0000000000
--- a/platform/ios/resources/api_mapbox_com-geotrust_2017.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/api_mapbox_staging.der b/platform/ios/resources/api_mapbox_staging.der
deleted file mode 100644
index 45f7df7c49..0000000000
--- a/platform/ios/resources/api_mapbox_staging.der
+++ /dev/null
Binary files differ
diff --git a/platform/ios/resources/da.lproj/Localizable.strings b/platform/ios/resources/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..dd384b21fc
--- /dev/null
+++ b/platform/ios/resources/da.lproj/Localizable.strings
@@ -0,0 +1,117 @@
+/* Accessibility hint */
+"ANNOTATION_A11Y_HINT" = "Vis mere info";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_DESC" = "Denne session kunne ikke gennemføres pga. data fejl. Den oprindelige forespørgsel var: %@";
+
+/* No comment provided by engineer. */
+"API_CLIENT_400_REASON" = "Status koden var %ld";
+
+/* No comment provided by engineer. */
+"CANCEL" = "Fortryd";
+
+/* Accessibility hint for closing the selected annotation’s callout view and returning to the map */
+"CLOSE_CALLOUT_A11Y_HINT" = "Retur til kortet";
+
+/* Accessibility hint */
+"COMPASS_A11Y_HINT" = "Fast nord";
+
+/* Accessibility label */
+"COMPASS_A11Y_LABEL" = "Kompas";
+
+/* Compass abbreviation for north */
+"COMPASS_NORTH" = "N";
+
+/* Instructions in Interface Builder designable; {key}, {plist file name} */
+"DESIGNABLE" = "For at vise et Mapbox-hosted kort her, angiv %1$@ til din access token i %2$@\n\nFor yderligere instruktion 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" = "Vis credits, feedback formular med mere";
+
+/* Accessibility label */
+"INFO_A11Y_LABEL" = "Om kortet";
+
+/* List separator */
+"LIST_SEPARATOR" = ", ";
+
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en ukendt fejl";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en fejl i kort formatteringen";
+
+/* Accessibility label */
+"LOGO_A11Y_LABEL" = "Mapbox";
+
+/* Accessibility label */
+"MAP_A11Y_LABEL" = "Kort";
+
+/* Map accessibility value; {number of visible annotations} */
+"MAP_A11Y_VALUE_ANNOTATIONS" = "%ld synlige kommentarer.";
+
+/* Map accessibility value; {list of visible places} */
+"MAP_A11Y_VALUE_PLACES" = "Synlige steder: %@.";
+
+/* Map accessibility value; {number of visible roads} */
+"MAP_A11Y_VALUE_ROADS" = "%ld synlige veje.";
+
+/* Map accessibility value; {zoom level} */
+"MAP_A11Y_VALUE_ZOOM" = "Zoom %dx.";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Kortet kunne ikke hentes på grund af en fejl";
+
+/* String format for accessibility value for road feature; {starting compass direction}, {ending compass direction} */
+"ROAD_DIRECTION_A11Y_FMT" = "%1$@ til %2$@";
+
+/* Accessibility value indicating that a road is a divided road (dual carriageway) */
+"ROAD_DIVIDED_A11Y_VALUE" = "Delt vej";
+
+/* Accessibility value indicating that a road is a one-way road */
+"ROAD_ONEWAY_A11Y_VALUE" = "Ensrettet";
+
+/* String format for accessibility value for road feature; {route number} */
+"ROAD_REF_A11Y_FMT" = "Rute %@";
+
+/* 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 %@ er nu tilgængelig:";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Kortet kunne ikke hentes fordi det enten ikke findes, eller ikke er kompatibelt.";
+
+/* Telemetry prompt message */
+"TELEMETRY_DISABLED_MSG" = "Du kan hjælpe med at gøre OpenStreetMap og Mapbox kort bedre ved at bidrage med annonyme bruger data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_OFF" = "Deltag ikke";
+
+/* Telemetry prompt button */
+"TELEMETRY_DISABLED_ON" = "Deltag";
+
+/* Telemetry prompt message */
+"TELEMETRY_ENABLED_MSG" = "Du hjælper med at gøre OpenStreetMap og Mapbox kort bedre ved at bidrage med annonyme bruger data.";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_OFF" = "Stop deltagelse";
+
+/* Telemetry prompt button */
+"TELEMETRY_ENABLED_ON" = "Fortsæt deltagelse";
+
+/* Telemetry prompt button */
+"TELEMETRY_MORE" = "Fortæl mig mere";
+
+/* Action in attribution sheet */
+"TELEMETRY_NAME" = "Mapbox Telemetry";
+
+/* Telemetry prompt title */
+"TELEMETRY_TITLE" = "Gør Mapbox kort bedre";
+
+/* Default user location annotation title */
+"USER_DOT_TITLE" = "Du er her";
+
diff --git a/platform/ios/resources/da.lproj/Localizable.stringsdict b/platform/ios/resources/da.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..296b8c88dd
--- /dev/null
+++ b/platform/ios/resources/da.lproj/Localizable.stringsdict
@@ -0,0 +1,54 @@
+<?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>MAP_A11Y_VALUE_ANNOTATIONS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%ld synlig kommentar</string>
+ <key>other</key>
+ <string>%ld synlige kommentarer</string>
+ </dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ROADS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%ld synlig vej</string>
+ <key>other</key>
+ <string>%ld synlige veje</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 %dx</string>
+ <key>other</key>
+ <string>Zoom %dx.</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
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/pt-PT.lproj/Localizable.strings b/platform/ios/resources/pt-PT.lproj/Localizable.strings
new file mode 100644
index 0000000000..f9a072666b
--- /dev/null
+++ b/platform/ios/resources/pt-PT.lproj/Localizable.strings
Binary files differ
diff --git a/platform/ios/resources/pt-PT.lproj/Localizable.stringsdict b/platform/ios/resources/pt-PT.lproj/Localizable.stringsdict
new file mode 100644
index 0000000000..f342bd58b5
--- /dev/null
+++ b/platform/ios/resources/pt-PT.lproj/Localizable.stringsdict
@@ -0,0 +1,54 @@
+<?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>MAP_A11Y_VALUE_ANNOTATIONS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%d erro reportado visível</string>
+ <key>other</key>
+ <string>%d erros reportados visíveis</string>
+ </dict>
+ </dict>
+ <key>MAP_A11Y_VALUE_ROADS</key>
+ <dict>
+ <key>NSStringLocalizedFormatKey</key>
+ <string>%#@count@</string>
+ <key>count</key>
+ <dict>
+ <key>NSStringFormatSpecTypeKey</key>
+ <string>NSStringPluralRuleType</string>
+ <key>NSStringFormatValueTypeKey</key>
+ <string>ld</string>
+ <key>one</key>
+ <string>%d estrada visível</string>
+ <key>other</key>
+ <string>%d estradas visíveis</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 %dx</string>
+ <key>other</key>
+ <string>Zoom %dx</string>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/platform/ios/resources/ru.lproj/Localizable.strings b/platform/ios/resources/ru.lproj/Localizable.strings
index 6d5ea9025e..b6ceffd520 100644
--- a/platform/ios/resources/ru.lproj/Localizable.strings
+++ b/platform/ios/resources/ru.lproj/Localizable.strings
@@ -1,44 +1,47 @@
/* 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:%@";
+"API_CLIENT_400_DESC" = "Во время обмена данными произошла ошибка. Оригинал запроса: %@";
/* No comment provided by engineer. */
-"API_CLIENT_400_REASON" = "The status code was %ld";
+"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" = "Вернуться на карту";
+"CLOSE_CALLOUT_A11Y_HINT" = "Вернуться к карте";
/* Accessibility hint */
-"COMPASS_A11Y_HINT" = "Повернуть карту на север";
+"COMPASS_A11Y_HINT" = "Развернуть карту на север";
/* Accessibility label */
"COMPASS_A11Y_LABEL" = "Компас";
/* Compass abbreviation for north */
-"COMPASS_NORTH" = "N";
+"COMPASS_NORTH" = "С";
/* Instructions in Interface Builder designable; {key}, {plist file name} */
-"DESIGNABLE" = "Для отображения здесь карт Mapbox задайте %1$@ к вашему токену доступа в %2$@\n\nПодробные инструкции см.:";
+"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" = "Показать авторство, форму обратной связи и многое другое";
+"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" = "Не удалось загрузить карту так как невозможно загрузить стиль.";
+"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки загрузки стиля.";
/* Accessibility label */
"LOGO_A11Y_LABEL" = "Mapbox";
@@ -46,23 +49,44 @@
/* 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" = "Масштаб %dx.";
/* 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";
+"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 лучше путем предоставления анонимных данных об использовании.";
+"TELEMETRY_DISABLED_MSG" = "Вы поможете улучшить карты OpenStreetMap и Mapbox, предоставляя обезличенные данные об использовании.";
/* Telemetry prompt button */
"TELEMETRY_DISABLED_OFF" = "Не участвовать";
@@ -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" = "Прекратить участие";
@@ -83,7 +107,7 @@
"TELEMETRY_MORE" = "Узнать больше";
/* Action in attribution sheet */
-"TELEMETRY_NAME" = "Mapbox телеметрия";
+"TELEMETRY_NAME" = "Телеметрия Mapbox";
/* Telemetry prompt title */
"TELEMETRY_TITLE" = "Сделать карты Mapbox лучше";
diff --git a/platform/ios/resources/ru.lproj/Localizable.stringsdict b/platform/ios/resources/ru.lproj/Localizable.stringsdict
index 81877703b1..fabd557780 100644
--- a/platform/ios/resources/ru.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/ru.lproj/Localizable.stringsdict
@@ -2,26 +2,30 @@
<!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>many</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>
@@ -29,13 +33,33 @@
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>one</key>
- <string>%d аннотация видны</string>
+ <string>Показана %d дорога</string>
<key>few</key>
- <string>%d аннотации видны</string>
+ <string>Показано %d дороги</string>
<key>many</key>
- <string>%d аннотаций видны</string>
+ <string>Показано %d дорог</string>
<key>other</key>
- <string>%d аннотации видны</string>
+ <string>Показано %d дорог</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>many</key>
+ <string>Масштаб %dx</string>
+ <key>other</key>
+ <string>Масштаб %dx</string>
</dict>
</dict>
</dict>
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/sv.lproj/Localizable.stringsdict b/platform/ios/resources/sv.lproj/Localizable.stringsdict
index 90115bd803..5f44d19b37 100644
--- a/platform/ios/resources/sv.lproj/Localizable.stringsdict
+++ b/platform/ios/resources/sv.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 annotering synlig</string>
<key>other</key>
- <string>Zoom %dx</string>
+ <string>%d annoteringar synliga</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 annotering synlig</string>
+ <string>%d väg synlig</string>
<key>other</key>
- <string>%d annoteringar synliga</string>
+ <string>%d vägar synliga</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>Zooma %dx</string>
+ <key>other</key>
+ <string>Zooma %dx</string>
</dict>
</dict>
</dict>
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/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift b/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift
index 34bbcb7666..d28b4e7d73 100644
--- a/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift
+++ b/platform/ios/scripts/script_resources/MapboxDemo/MapboxDemo/ViewController.swift
@@ -110,7 +110,7 @@ class ViewController: UIViewController {
// You can obtain your own access token from the
// [Mapbox account page](https://www.mapbox.com/studio/account/tokens/)
// and add it to this application's Info.plist as the value for MGLMapboxAccessToken
- if MGLAccountManager.accessToken() == "pk.eyJ1IjoibWFwYm94IiwiYSI6ImNqYThuNnZ3NTA5MGMyd3F1cmF1eW1xaGEifQ.TdBTSHHPeT1pfLZ_6x_1vA" {
+ if MGLAccountManager.accessToken == "pk.eyJ1IjoibWFwYm94IiwiYSI6ImNqYThuNnZ3NTA5MGMyd3F1cmF1eW1xaGEifQ.TdBTSHHPeT1pfLZ_6x_1vA" {
accessTokenWarningView.isHidden = false
}
diff --git a/platform/ios/src/MGLAPIClient.h b/platform/ios/src/MGLAPIClient.h
deleted file mode 100644
index 4e5ea3b5e0..0000000000
--- a/platform/ios/src/MGLAPIClient.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#import <Foundation/Foundation.h>
-
-#import "MGLMapboxEvents.h"
-#import "MGLTypes.h"
-
-NS_ASSUME_NONNULL_BEGIN
-
-@interface MGLAPIClient : NSObject <NSURLSessionDelegate>
-
-- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
-- (void)postEvent:(MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler;
-
-@end
-
-NS_ASSUME_NONNULL_END
diff --git a/platform/ios/src/MGLAPIClient.m b/platform/ios/src/MGLAPIClient.m
deleted file mode 100644
index 68e78835c3..0000000000
--- a/platform/ios/src/MGLAPIClient.m
+++ /dev/null
@@ -1,218 +0,0 @@
-#import "MGLAPIClient.h"
-#import "NSBundle+MGLAdditions.h"
-#import "NSData+MGLAdditions.h"
-#import "MGLAccountManager.h"
-#import "MGLNetworkConfiguration.h"
-
-static NSString * const MGLAPIClientUserAgentBase = @"MapboxEventsiOS";
-static NSString * const MGLAPIClientBaseURL = @"https://events.mapbox.com";
-static NSString * const MGLAPIClientChinaBaseURL = @"https://events.mapbox.cn";
-static NSString * const MGLAPIClientEventsPath = @"events/v2";
-
-static NSString * const MGLAPIClientHeaderFieldUserAgentKey = @"User-Agent";
-static NSString * const MGLAPIClientHeaderFieldContentTypeKey = @"Content-Type";
-static NSString * const MGLAPIClientHeaderFieldContentTypeValue = @"application/json";
-static NSString * const MGLAPIClientHeaderFieldContentEncodingKey = @"Content-Encoding";
-static NSString * const MGLAPIClientHTTPMethodPost = @"POST";
-
-@interface MGLAPIClient ()
-
-@property (nonatomic, copy) NSURLSession *session;
-@property (nonatomic, copy) NSURL *baseURL;
-@property (nonatomic, copy) NSData *digicertCert_2016;
-@property (nonatomic, copy) NSData *geoTrustCert_2016;
-@property (nonatomic, copy) NSData *digicertCert_2017;
-@property (nonatomic, copy) NSData *geoTrustCert_2017;
-@property (nonatomic, copy) NSData *digicertCert_cn_2018;
-@property (nonatomic, copy) NSData *geoTrustCert_cn_2018;
-@property (nonatomic, copy) NSData *testServerCert;
-@property (nonatomic, copy) NSString *userAgent;
-@property (nonatomic) BOOL usesTestServer;
-
-@end
-
-@implementation MGLAPIClient
-
-- (instancetype)init {
- self = [super init];
- if (self) {
- _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
- delegate:self delegateQueue:nil];
- [self loadCertificates];
- [self setupBaseURL];
- [self setupUserAgent];
- }
- return self;
-}
-
-#pragma mark Public API
-
-- (void)postEvents:(nonnull NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
- __block NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:[self requestForEvents:events]
- completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
- NSError *statusError = nil;
- if (httpResponse.statusCode >= 400) {
- NSString *description = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_DESC", nil, nil, @"The session data task failed. Original request was: %@", nil), dataTask.originalRequest];
- NSString *reason = [NSString stringWithFormat:NSLocalizedStringWithDefaultValue(@"API_CLIENT_400_REASON", nil, nil, @"The status code was %ld", nil), (long)httpResponse.statusCode];
- NSDictionary *userInfo = @{NSLocalizedDescriptionKey: description,
- NSLocalizedFailureReasonErrorKey: reason};
- statusError = [NSError errorWithDomain:MGLErrorDomain code:1 userInfo:userInfo];
- }
- if (completionHandler) {
- error = error ?: statusError;
- completionHandler(error);
- }
- dataTask = nil;
- }];
- [dataTask resume];
-}
-
-- (void)postEvent:(nonnull MGLMapboxEventAttributes *)event completionHandler:(nullable void (^)(NSError * _Nullable error))completionHandler {
- [self postEvents:@[event] completionHandler:completionHandler];
-}
-
-#pragma mark Utilities
-
-- (NSURLRequest *)requestForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- NSString *path = [NSString stringWithFormat:@"%@?access_token=%@", MGLAPIClientEventsPath, [MGLAccountManager accessToken]];
- NSURL *url = [NSURL URLWithString:path relativeToURL:self.baseURL];
- NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
- [request setValue:self.userAgent forHTTPHeaderField:MGLAPIClientHeaderFieldUserAgentKey];
- [request setValue:MGLAPIClientHeaderFieldContentTypeValue forHTTPHeaderField:MGLAPIClientHeaderFieldContentTypeKey];
- [request setHTTPMethod:MGLAPIClientHTTPMethodPost];
-
- NSData *jsonData = [self serializedDataForEvents:events];
-
- // Compressing less than 3 events can have a negative impact on the size.
- if (events.count > 2) {
- NSData *compressedData = [jsonData mgl_compressedData];
- [request setValue:@"deflate" forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
- [request setHTTPBody:compressedData];
- }
-
- // Set JSON data if events.count were less than 3 or something went wrong with compressing HTTP body data.
- if (!request.HTTPBody) {
- [request setValue:nil forHTTPHeaderField:MGLAPIClientHeaderFieldContentEncodingKey];
- [request setHTTPBody:jsonData];
- }
-
- return [request copy];
-}
-
-- (void)setupBaseURL {
- NSString *testServerURLString = [[NSUserDefaults standardUserDefaults] stringForKey:@"MGLTelemetryTestServerURL"];
- NSURL *testServerURL = [NSURL URLWithString:testServerURLString];
- if (testServerURL && [testServerURL.scheme isEqualToString:@"https"]) {
- self.baseURL = testServerURL;
- self.usesTestServer = YES;
- } else if ([[[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxAPIBaseURL"] isEqualToString:MGLChinaMapboxAPIBaseURL]) {
- self.baseURL = [NSURL URLWithString:MGLAPIClientChinaBaseURL];
- } else {
- self.baseURL = [NSURL URLWithString:MGLAPIClientBaseURL];
- }
-}
-
-- (void)loadCertificates {
- NSData *certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2016"];
- self.geoTrustCert_2016 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2016"];
- self.digicertCert_2016 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-geotrust_2017"];
- self.geoTrustCert_2017 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_com-digicert_2017"];
- self.digicertCert_2017 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_cn-geotrust_2018"];
- self.geoTrustCert_cn_2018 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_cn-digicert_2018"];
- self.digicertCert_cn_2018 = certificate;
- [self loadCertificate:&certificate withResource:@"api_mapbox_staging"];
- self.testServerCert = certificate;
-}
-
-- (void)loadCertificate:(NSData **)certificate withResource:(NSString *)resource {
- NSBundle *frameworkBundle = [NSBundle mgl_frameworkBundle];
- NSString *cerPath = [frameworkBundle pathForResource:resource ofType:@"der"];
- if (cerPath != nil) {
- *certificate = [NSData dataWithContentsOfFile:cerPath];
- }
-}
-
-- (void)setupUserAgent {
- NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
- NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
- NSString *appBuildNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
- NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
- NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
- NSString *sdkVersion = semanticVersion ?: shortVersion;
- _userAgent = [NSString stringWithFormat:@"%@/%@/%@ %@/%@", appName, appVersion, appBuildNumber, MGLAPIClientUserAgentBase, sdkVersion];
-}
-
-#pragma mark - JSON Serialization
-
-- (NSData *)serializedDataForEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- return [NSJSONSerialization dataWithJSONObject:events options:0 error:nil];
-}
-
-#pragma mark NSURLSessionDelegate
-
-- (BOOL)evaluateCertificateWithCertificateData:(NSData *)certificateData keyCount:(CFIndex)keyCount serverTrust:(SecTrustRef)serverTrust challenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
- for (int lc = 0; lc < keyCount; lc++) {
- SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, lc);
- NSData *remoteCertificateData = CFBridgingRelease(SecCertificateCopyData(certificate));
- if ([remoteCertificateData isEqualToData:certificateData]) {
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- return YES;
- }
- }
- return NO;
-}
-
-- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^) (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
-
- if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
- SecTrustRef serverTrust = [[challenge protectionSpace] serverTrust];
- SecTrustResultType trustResult;
-
- // Validate the certificate chain with the device's trust store anyway this *might* use revocation checking
- SecTrustEvaluate(serverTrust, &trustResult);
-
- BOOL found = NO; // For clarity; we start in a state where the challange has not been completed and no certificate has been found
-
- if (trustResult == kSecTrustResultUnspecified) {
- // Look for a pinned certificate in the server's certificate chain
- CFIndex numKeys = SecTrustGetCertificateCount(serverTrust);
-
- // Check certs in the following order: digicert 2016, digicert 2017, digicert CN 2018, geotrust 2016, geotrust 2017, geotrust CN 2018
- found = [self evaluateCertificateWithCertificateData:self.digicertCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.digicertCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.digicertCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2016 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_2017 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- if (!found) {
- found = [self evaluateCertificateWithCertificateData:self.geoTrustCert_cn_2018 keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
-
- // If challenge can't be completed with any of the above certs, then try the test server if the app is configured to use the test server
- if (!found && _usesTestServer) {
- found = [self evaluateCertificateWithCertificateData:self.testServerCert keyCount:numKeys serverTrust:serverTrust challenge:challenge completionHandler:completionHandler];
- }
- }
-
- if (!found) {
- // No certificate was found so cancel the connection.
- completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
- }
- }
-}
-
-@end
diff --git a/platform/ios/src/MGLAnnotationContainerView.h b/platform/ios/src/MGLAnnotationContainerView.h
index 90d2964831..ccec3052a6 100644
--- a/platform/ios/src/MGLAnnotationContainerView.h
+++ b/platform/ios/src/MGLAnnotationContainerView.h
@@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)annotationContainerViewWithAnnotationContainerView:(MGLAnnotationContainerView *)annotationContainerView;
-- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews;
+- (void)addSubviews:(NSArray<MGLAnnotationView *> *)subviews;
@end
diff --git a/platform/ios/src/MGLAnnotationContainerView.m b/platform/ios/src/MGLAnnotationContainerView.m
index 9a823c839c..6c82a1836d 100644
--- a/platform/ios/src/MGLAnnotationContainerView.m
+++ b/platform/ios/src/MGLAnnotationContainerView.m
@@ -3,7 +3,7 @@
@interface MGLAnnotationContainerView ()
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews;
+@property (nonatomic) NSMutableArray<MGLAnnotationView *> *annotationViews;
@end
@@ -26,7 +26,7 @@
return newAnnotationContainerView;
}
-- (void)addSubviews:(NS_ARRAY_OF(MGLAnnotationView *) *)subviews
+- (void)addSubviews:(NSArray<MGLAnnotationView *> *)subviews
{
for (MGLAnnotationView *view in subviews)
{
diff --git a/platform/ios/src/MGLAnnotationContainerView_Private.h b/platform/ios/src/MGLAnnotationContainerView_Private.h
index 007b03550b..9dce54842d 100644
--- a/platform/ios/src/MGLAnnotationContainerView_Private.h
+++ b/platform/ios/src/MGLAnnotationContainerView_Private.h
@@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface MGLAnnotationContainerView (Private)
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *annotationViews;
+@property (nonatomic) NSMutableArray<MGLAnnotationView *> *annotationViews;
@end
diff --git a/platform/ios/src/MGLAnnotationView.h b/platform/ios/src/MGLAnnotationView.h
index 4fa0f196ab..57d97e56c1 100644
--- a/platform/ios/src/MGLAnnotationView.h
+++ b/platform/ios/src/MGLAnnotationView.h
@@ -169,8 +169,12 @@ MGL_EXPORT
value of this property is `NO` or the map’s pitch is zero, the annotation view
remains the same size regardless of its position on-screen.
- The default value of this property is `YES`. Set this property to `NO` if the
- view’s legibility is important.
+ The default value of this property is `NO`. Keep this property set to `NO` if
+ the view’s legibility is important.
+
+ @note Scaling many on-screen annotation views can contribute to poor map
+ performance. Consider keeping this property disabled 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..1c53ba507a 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;
@@ -44,7 +45,7 @@
_lastAppliedScaleTransform = CATransform3DIdentity;
_annotation = annotation;
_reuseIdentifier = [reuseIdentifier copy];
- _scalesWithViewingDistance = YES;
+ _scalesWithViewingDistance = NO;
_enabled = YES;
}
@@ -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/MGLCalloutView.h b/platform/ios/src/MGLCalloutView.h
index 0481a39680..689b159fd7 100644
--- a/platform/ios/src/MGLCalloutView.h
+++ b/platform/ios/src/MGLCalloutView.h
@@ -39,7 +39,14 @@ NS_ASSUME_NONNULL_BEGIN
Presents a callout view by adding it to `view` and pointing at the given rect
of `view`’s bounds. Constrains the callout to the bounds of the given view.
*/
-- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated __attribute__((unavailable("Use -presentCalloutFromRect:inView:constrainedToRect:animated: instead.")));
+
+
+/**
+ Presents a callout view by adding it to `view` and pointing at the given rect
+ of `view`’s bounds. Constrains the callout to the rect in the space of `view`.
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated;
/**
Dismisses the callout view.
@@ -49,6 +56,24 @@ NS_ASSUME_NONNULL_BEGIN
@optional
/**
+ If implemented, should provide margins to expand the rect the callout is presented from.
+
+ These are used to determine positioning. Currently only the top and bottom properties of the return
+ value are used. For example, `{ .top = -50.0, .left = -10.0, .bottom = 0.0, .right = -10.0 }` indicates
+ a 50 point margin above the presentation origin rect (and 10 point margins to the left and the right)
+ in which the callout is assumed to be displayed.
+
+ There are no assumed defaults for these margins, as they should be calculated from the callout that
+ is to be presented. For example, `SMCalloutView` generates the top margin from the callout height, but
+ the left and right margins from a minimum width that the callout should have.
+
+ @param rect Rect that the callout is presented from. This should be the same as the one passed in
+ `-[MGLCalloutView presentCalloutFromRect:inView:constrainedToRect:animated:]`
+ @return `UIEdgeInsets` representing the margins. Values should be negative.
+ */
+- (UIEdgeInsets)marginInsetsHintForPresentationFromRect:(CGRect)rect NS_SWIFT_NAME(marginInsetsHintForPresentation(from:));
+
+/**
A Boolean value indicating whether the callout view should be anchored to
the corresponding annotation. You can adjust the callout view’s precise location by
overriding -[UIView setCenter:]. The callout view will not be anchored to the
diff --git a/platform/ios/src/MGLLocationManager.h b/platform/ios/src/MGLLocationManager.h
deleted file mode 100644
index ea23801813..0000000000
--- a/platform/ios/src/MGLLocationManager.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#import <Foundation/Foundation.h>
-#import <CoreLocation/CoreLocation.h>
-
-@protocol MGLLocationManagerDelegate;
-
-@interface MGLLocationManager : NSObject <CLLocationManagerDelegate>
-
-@property (nonatomic, weak) id<MGLLocationManagerDelegate> delegate;
-
-- (void)startUpdatingLocation;
-- (void)stopUpdatingLocation;
-
-@end
-
-@protocol MGLLocationManagerDelegate <NSObject>
-
-@optional
-
-- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations;
-- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager;
-- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager;
-- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager;
-- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager;
-
-@end
diff --git a/platform/ios/src/MGLLocationManager.m b/platform/ios/src/MGLLocationManager.m
deleted file mode 100644
index 85ef4ca489..0000000000
--- a/platform/ios/src/MGLLocationManager.m
+++ /dev/null
@@ -1,175 +0,0 @@
-#import "MGLLocationManager.h"
-#import "MGLTelemetryConfig.h"
-#import <UIKit/UIKit.h>
-
-static const NSTimeInterval MGLLocationManagerHibernationTimeout = 300.0;
-static const NSTimeInterval MGLLocationManagerHibernationPollInterval = 5.0;
-static const CLLocationDistance MGLLocationManagerDistanceFilter = 5.0;
-static NSString * const MGLLocationManagerRegionIdentifier = @"MGLLocationManagerRegionIdentifier.fence.center";
-
-@interface MGLLocationManager ()
-
-@property (nonatomic) CLLocationManager *standardLocationManager;
-@property (nonatomic) BOOL hostAppHasBackgroundCapability;
-@property (nonatomic, getter=isUpdatingLocation) BOOL updatingLocation;
-@property (nonatomic) NSDate *backgroundLocationServiceTimeoutAllowedDate;
-@property (nonatomic) NSTimer *backgroundLocationServiceTimeoutTimer;
-
-@end
-
-@implementation MGLLocationManager
-
-- (instancetype)init {
- self = [super init];
- if (self) {
- NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
- _hostAppHasBackgroundCapability = [backgroundModes containsObject:@"location"];
- }
- return self;
-}
-
-- (void)startUpdatingLocation {
- if ([self isUpdatingLocation]) {
- return;
- }
-
- [self configurePassiveStandardLocationManager];
- [self startLocationServices];
-}
-
-- (void)stopUpdatingLocation {
- if ([self isUpdatingLocation]) {
- [self.standardLocationManager stopUpdatingLocation];
- [self.standardLocationManager stopMonitoringSignificantLocationChanges];
- self.updatingLocation = NO;
- if ([self.delegate respondsToSelector:@selector(locationManagerDidStopLocationUpdates:)]) {
- [self.delegate locationManagerDidStopLocationUpdates:self];
- }
- }
- if(self.standardLocationManager.monitoredRegions.count > 0) {
- for(CLRegion *region in self.standardLocationManager.monitoredRegions) {
- if([region.identifier isEqualToString:MGLLocationManagerRegionIdentifier]) {
- [self.standardLocationManager stopMonitoringForRegion:region];
- }
- }
- }
-}
-
-#pragma mark - Utilities
-
-- (void)configurePassiveStandardLocationManager {
- if (!self.standardLocationManager) {
- CLLocationManager *standardLocationManager = [[CLLocationManager alloc] init];
- standardLocationManager.delegate = self;
- standardLocationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
- standardLocationManager.distanceFilter = MGLLocationManagerDistanceFilter;
- self.standardLocationManager = standardLocationManager;
- }
-}
-
-- (void)startLocationServices {
- CLAuthorizationStatus authorizationStatus = [CLLocationManager authorizationStatus];
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
- BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorizedAlways;
-#else
- BOOL authorizedAlways = authorizationStatus == kCLAuthorizationStatusAuthorized;
-#endif
- if (authorizedAlways || authorizationStatus == kCLAuthorizationStatusAuthorizedWhenInUse) {
- // If the host app can run in the background with `always` location permissions then allow background
- // updates and start the significant location change service and background timeout timer
- if (self.hostAppHasBackgroundCapability && authorizedAlways) {
- [self.standardLocationManager startMonitoringSignificantLocationChanges];
- [self startBackgroundTimeoutTimer];
- // On iOS 9 and above also allow background location updates
- if ([self.standardLocationManager respondsToSelector:@selector(allowsBackgroundLocationUpdates)]) {
- self.standardLocationManager.allowsBackgroundLocationUpdates = YES;
- }
- }
-
- [self.standardLocationManager startUpdatingLocation];
- self.updatingLocation = YES;
- if ([self.delegate respondsToSelector:@selector(locationManagerDidStartLocationUpdates:)]) {
- [self.delegate locationManagerDidStartLocationUpdates:self];
- }
- }
-}
-
-- (void)timeoutAllowedCheck {
- if (self.backgroundLocationServiceTimeoutAllowedDate == nil) {
- return;
- }
-
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive ||
- [UIApplication sharedApplication].applicationState == UIApplicationStateInactive ) {
- [self startBackgroundTimeoutTimer];
- return;
- }
-
- NSTimeInterval timeIntervalSinceTimeoutAllowed = [[NSDate date] timeIntervalSinceDate:self.backgroundLocationServiceTimeoutAllowedDate];
- if (timeIntervalSinceTimeoutAllowed > 0) {
- [self.standardLocationManager stopUpdatingLocation];
- self.backgroundLocationServiceTimeoutAllowedDate = nil;
- if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidTimeout:)]) {
- [self.delegate locationManagerBackgroundLocationUpdatesDidTimeout:self];
- }
- }
-}
-
-- (void)startBackgroundTimeoutTimer {
- [self.backgroundLocationServiceTimeoutTimer invalidate];
- self.backgroundLocationServiceTimeoutAllowedDate = [[NSDate date] dateByAddingTimeInterval:MGLLocationManagerHibernationTimeout];
- self.backgroundLocationServiceTimeoutTimer = [NSTimer scheduledTimerWithTimeInterval:MGLLocationManagerHibernationPollInterval target:self selector:@selector(timeoutAllowedCheck) userInfo:nil repeats:YES];
-}
-
-- (void)establishRegionMonitoringForLocation:(CLLocation *)location {
- CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius identifier:MGLLocationManagerRegionIdentifier];
- region.notifyOnEntry = NO;
- region.notifyOnExit = YES;
- [self.standardLocationManager startMonitoringForRegion:region];
-}
-
-#pragma mark - CLLocationManagerDelegate
-
-- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
- switch (status) {
-#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
- case kCLAuthorizationStatusAuthorizedAlways:
-#else
- case kCLAuthorizationStatusAuthorized:
-#endif
- case kCLAuthorizationStatusAuthorizedWhenInUse:
- [self startUpdatingLocation];
- break;
- default:
- [self stopUpdatingLocation];
- break;
- }
-}
-
-- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations {
- CLLocation *location = locations.lastObject;
- if (location.speed > 0.0) {
- [self startBackgroundTimeoutTimer];
- }
- if (self.standardLocationManager.monitoredRegions.count == 0 || location.horizontalAccuracy < MGLTelemetryConfig.sharedConfig.MGLLocationManagerHibernationRadius) {
- [self establishRegionMonitoringForLocation:location];
- }
- if ([self.delegate respondsToSelector:@selector(locationManager:didUpdateLocations:)]) {
- [self.delegate locationManager:self didUpdateLocations:locations];
- }
-}
-
-- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
- [self startBackgroundTimeoutTimer];
- [self.standardLocationManager startUpdatingLocation];
-}
-
-- (void)locationManagerDidPauseLocationUpdates:(CLLocationManager *)manager {
- if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
- if ([self.delegate respondsToSelector:@selector(locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:)]) {
- [self.delegate locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:self];
- }
- }
-}
-
-@end
diff --git a/platform/ios/src/MGLMapAccessibilityElement.mm b/platform/ios/src/MGLMapAccessibilityElement.mm
index 4e5f165fbf..c1cc5304d7 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 "MGLVectorTileSource_Private.h"
+
+#import "NSBundle+MGLAdditions.h"
+#import "NSOrthography+MGLAdditions.h"
+#import "NSString+MGLAdditions.h"
@implementation MGLMapAccessibilityElement
@@ -45,27 +48,17 @@
if (self = [super initWithAccessibilityContainer:container]) {
_feature = feature;
- NSString *languageCode = [MGLVectorSource preferredMapboxStreetsLanguage];
+ NSString *languageCode = [MGLVectorTileSource 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.
+ NSString *dominantScript = [NSOrthography mgl_dominantScriptForMapboxStreetsLanguage:languageCode];
+ name = [name mgl_stringByTransliteratingIntoScript:dominantScript];
+
self.accessibilityLabel = name;
}
return self;
diff --git a/platform/ios/src/MGLMapView+IBAdditions.h b/platform/ios/src/MGLMapView+IBAdditions.h
index 6d5351df2b..64016e8319 100644
--- a/platform/ios/src/MGLMapView+IBAdditions.h
+++ b/platform/ios/src/MGLMapView+IBAdditions.h
@@ -44,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) IBInspectable BOOL allowsTilting;
@property (nonatomic) IBInspectable BOOL showsUserLocation;
@property (nonatomic) IBInspectable BOOL showsHeading;
+@property (nonatomic) IBInspectable BOOL showsScale;
@end
diff --git a/platform/ios/src/MGLMapView.h b/platform/ios/src/MGLMapView.h
index 3c5aa2c122..bde8e6a71d 100644
--- a/platform/ios/src/MGLMapView.h
+++ b/platform/ios/src/MGLMapView.h
@@ -189,13 +189,7 @@ MGL_EXPORT IB_DESIGNABLE
*/
@property (nonatomic, readonly, nullable) MGLStyle *style;
-/**
- URLs of the styles bundled with the library.
-
- @deprecated Call the relevant class method of `MGLStyle` for the URL of a
- particular default style.
- */
-@property (nonatomic, readonly) NS_ARRAY_OF(NSURL *) *bundledStyleURLs __attribute__((deprecated("Call the relevant class method of MGLStyle for the URL of a particular default style.")));
+@property (nonatomic, readonly) NSArray<NSURL *> *bundledStyleURLs __attribute__((unavailable("Call the relevant class method of MGLStyle for the URL of a particular default style.")));
/**
URL of the style currently displayed in the receiver.
@@ -224,11 +218,20 @@ MGL_EXPORT IB_DESIGNABLE
the server, calling this method does not necessarily ensure that the map view
reflects those changes.
*/
-- (IBAction)reloadStyle:(id)sender;
+- (IBAction)reloadStyle:(nullable id)sender;
+
+/**
+ A Boolean value indicating whether the map may display scale information.
+
+ The scale bar may not be shown at all zoom levels. The view controlled by this
+ property is available at `scaleBar`. The default value of this property is
+ `NO`.
+ */
+@property (nonatomic, assign) BOOL showsScale;
/**
A control indicating the scale of the map. The scale bar is positioned in the
- upper-left corner. The scale bar is hidden by default.
+ upper-left corner. Enable the scale bar via `showsScale`.
*/
@property (nonatomic, readonly) UIView *scaleBar;
@@ -283,17 +286,13 @@ MGL_EXPORT IB_DESIGNABLE
*/
- (IBAction)showAttribution:(id)sender;
-/// :nodoc: Support for style classes has been removed. This property always returns an empty array.
-@property (nonatomic) NS_ARRAY_OF(NSString *) *styleClasses __attribute__((deprecated("This property is non-functional.")));
+@property (nonatomic) NSArray<NSString *> *styleClasses __attribute__((unavailable("Support for style classes has been removed.")));
-/// :nodoc: Support for style classes has been removed. This property always returns NO.
-- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (BOOL)hasStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
-/// :nodoc: Support for style classes has been removed. This property is a no-op.
-- (void)addStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (void)addStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
-/// :nodoc: Support for style classes has been removed. This property is a no-op.
-- (void)removeStyleClass:(NSString *)styleClass __attribute__((deprecated("This method is non-functional.")));
+- (void)removeStyleClass:(NSString *)styleClass __attribute__((unavailable("Support for style classes has been removed.")));
#pragma mark Displaying the User’s Location
@@ -494,6 +493,19 @@ MGL_EXPORT IB_DESIGNABLE
@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
/**
+ A Boolean value that determines whether the user will receive haptic feedback
+ for certain interactions with the map.
+
+ When this property is set to `YES`, the default, a `UIImpactFeedbackStyleLight`
+ haptic feedback event be played when the user rotates the map to due north
+ (0°).
+
+ This feature requires a device that supports haptic feedback, running iOS 10 or
+ newer.
+ */
+@property(nonatomic, getter=isHapticFeedbackEnabled) BOOL hapticFeedbackEnabled;
+
+/**
A floating-point value that determines the rate of deceleration after the user
lifts their finger.
@@ -669,11 +681,10 @@ MGL_EXPORT IB_DESIGNABLE
want to animate the change, call `-setVisibleCoordinateBounds:animated:`
instead.
- If a longitude is less than −180 degrees or greater than 180 degrees, the visible
- bounds straddles the antimeridian or international date line.
-
- For example, a visible bounds that stretches from Tokyo to San Francisco would have
- coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+ If a longitude is less than −180 degrees or greater than 180 degrees, the
+ visible bounds straddles the antimeridian or international date line. For
+ example, if both Tokyo and San Francisco are visible, the visible bounds might
+ extend from (35.68476, −220.24257) to (37.78428, −122.41310).
*/
@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
@@ -681,11 +692,10 @@ MGL_EXPORT IB_DESIGNABLE
Changes the receiver’s viewport to fit the given coordinate bounds,
optionally animating the change.
- To make the visible bounds go across the antimeridian or international date line,
- specify some longitudes less than −180 degrees or greater than 180 degrees.
-
- For example, a visible bounds that stretches from Tokyo to San Francisco would have
- coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+ To bring both sides of the antimeridian or international date line into view,
+ specify some longitudes less than −180 degrees or greater than 180 degrees. For
+ example, to show both Tokyo and San Francisco simultaneously, you could set the
+ visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310).
@param bounds The bounds that the viewport will show in its entirety.
@param animated Specify `YES` to animate the change by smoothly scrolling
@@ -696,6 +706,11 @@ MGL_EXPORT IB_DESIGNABLE
/**
Changes the receiver’s viewport to fit the given coordinate bounds and
optionally some additional padding on each side.
+
+ To bring both sides of the antimeridian or international date line into view,
+ specify some longitudes less than −180 degrees or greater than 180 degrees. For
+ example, to show both Tokyo and San Francisco simultaneously, you could set the
+ visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310).
@param bounds The bounds that the viewport will show in its entirety.
@param insets The minimum padding (in screen points) that will be visible
@@ -708,6 +723,11 @@ MGL_EXPORT IB_DESIGNABLE
/**
Changes the receiver’s viewport to fit all of the given coordinates and
optionally some additional padding on each side.
+
+ To bring both sides of the antimeridian or international date line into view,
+ specify some longitudes less than −180 degrees or greater than 180 degrees. For
+ example, to show both Tokyo and San Francisco simultaneously, you could set the
+ visible coordinates to (35.68476, −220.24257) and (37.78428, −122.41310).
@param coordinates The coordinates that the viewport will show.
@param count The number of coordinates. This number must not be greater than
@@ -722,6 +742,11 @@ MGL_EXPORT IB_DESIGNABLE
/**
Changes the receiver’s viewport to fit all of the given coordinates and
optionally some additional padding on each side.
+
+ To bring both sides of the antimeridian or international date line into view,
+ specify some longitudes less than −180 degrees or greater than 180 degrees. For
+ example, to show both Tokyo and San Francisco simultaneously, you could set the
+ visible coordinates to (35.68476, −220.24257) and (37.78428, −122.41310).
@param coordinates The coordinates that the viewport will show.
@param count The number of coordinates. This number must not be greater than
@@ -748,7 +773,7 @@ MGL_EXPORT IB_DESIGNABLE
@param animated `YES` if you want the map region change to be animated, or `NO`
if you want the map to display the new region immediately without animations.
*/
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated;
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated;
/**
Sets the visible region so that the map displays the specified annotations with
@@ -763,7 +788,7 @@ MGL_EXPORT IB_DESIGNABLE
@param animated `YES` if you want the map region change to be animated, or `NO`
if you want the map to display the new region immediately without animations.
*/
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated;
/**
A camera representing the current viewpoint of the map.
@@ -1004,6 +1029,9 @@ MGL_EXPORT IB_DESIGNABLE
/**
Converts a rectangle in the given view’s coordinate system to a geographic
bounding box.
+
+ If a longitude is less than −180 degrees or greater than 180 degrees, the
+ bounding box straddles the antimeridian or international date line.
@param rect The rectangle to convert.
@param view The view in whose coordinate system the rectangle is expressed.
@@ -1048,7 +1076,7 @@ MGL_EXPORT IB_DESIGNABLE
annotations are associated with the map view, the value of this property is
`nil`.
*/
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
+@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *annotations;
/**
Adds an annotation to the map view.
@@ -1076,7 +1104,7 @@ MGL_EXPORT IB_DESIGNABLE
must conform to the `MGLAnnotation` protocol. The map view retains each
individual annotation object.
*/
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations;
/**
Removes an annotation from the map view, deselecting it if it is selected.
@@ -1101,7 +1129,7 @@ MGL_EXPORT IB_DESIGNABLE
@param annotations The array of annotation objects to remove. Objects in the
array must conform to the `MGLAnnotation` protocol.
*/
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations;
/**
Returns an `MGLAnnotationView` if the given annotation is currently associated
@@ -1152,7 +1180,7 @@ MGL_EXPORT IB_DESIGNABLE
annotations are associated with the map view or if no annotations associated
with the map view are currently visible, the value of this property is `nil`.
*/
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations;
+@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *visibleAnnotations;
/**
Returns the list of annotations associated with the receiver that intersect with
@@ -1163,7 +1191,7 @@ MGL_EXPORT IB_DESIGNABLE
no annotations associated with the map view are currently visible in the
rectangle.
*/
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect;
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect;
#pragma mark Managing Annotation Selections
@@ -1172,17 +1200,32 @@ MGL_EXPORT IB_DESIGNABLE
Assigning a new array to this property selects only the first annotation in
the array.
+
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the camera
+ will animate to bring the annotation and its callout just on screen. If you
+ need finer control, consider using `-selectAnnotation:animated:`.
+
+ @note In versions prior to `4.0.0` if the annotation was offscreen it was not
+ selected.
*/
-@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
+@property (nonatomic, copy) NSArray<id <MGLAnnotation>> *selectedAnnotations;
/**
- Selects an annotation and displays a callout view for it.
+ Selects an annotation and displays its callout view.
- If the given annotation is not visible within the current viewport, this
- method has no effect.
+ The `animated` parameter determines whether the map is panned to bring the
+ annotation on-screen, specifically:
+
+ | `animated` parameter | Effect |
+ |------------------|--------|
+ | `NO` | The annotation is selected, and the callout is presented. However the map is not panned to bring the annotation or callout onscreen. The presentation of the callout is animated. |
+ | `YES` | The annotation is selected, and the callout is presented. If the annotation is offscreen *and* is of type `MGLPointAnnotation`, the map is panned so that the annotation and its callout are brought just onscreen. The annotation is *not* centered within the viewport. |
@param annotation The annotation object to select.
- @param animated If `YES`, the callout view is animated into position.
+ @param animated If `YES`, the annotation and callout view are animated on-screen.
+
+ @note In versions prior to `4.0.0` selecting an offscreen annotation did not
+ change the camera.
*/
- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated;
@@ -1203,7 +1246,7 @@ MGL_EXPORT IB_DESIGNABLE
overlays are associated with the map view, the value of this property is
empty array.
*/
-@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays;
+@property (nonatomic, readonly, nonnull) NSArray<id <MGLOverlay>> *overlays;
/**
Adds a single overlay object to the map.
@@ -1222,7 +1265,7 @@ MGL_EXPORT IB_DESIGNABLE
@param overlays An array of objects, each of which must conform to the
`MGLOverlay` protocol.
*/
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays;
/**
Removes a single overlay object from the map.
@@ -1242,7 +1285,7 @@ MGL_EXPORT IB_DESIGNABLE
@param overlays An array of objects, each of which conforms to the `MGLOverlay`
protocol.
*/
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays;
#pragma mark Accessing the Underlying Map Data
@@ -1258,7 +1301,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point NS_SWIFT_NAME(visibleFeatures(at:));
/**
Returns an array of rendered map features that intersect with a given point,
@@ -1277,7 +1320,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
/**
Returns an array of rendered map features that intersect with a given point,
@@ -1286,9 +1329,9 @@ MGL_EXPORT IB_DESIGNABLE
Each object in the returned array represents a feature rendered by the
current style and provides access to attributes specified by the relevant map
content sources. The returned array includes features loaded by
- `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
- from `MGLRasterSource` objects, or from image, video, or canvas sources, which
- are unsupported by this SDK.
+ `MGLShapeSource` and `MGLVectorTileSource` objects but does not include
+ anything from `MGLRasterTileSource` objects, or from video or canvas sources,
+ which are unsupported by this SDK.
The returned features are drawn by a style layer in the current style. For
example, suppose the current style uses the
@@ -1320,7 +1363,7 @@ MGL_EXPORT IB_DESIGNABLE
Only visible features are returned. To obtain features regardless of
visibility, use the
- `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLVectorTileSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
The returned features may also include features corresponding to annotations.
@@ -1344,7 +1387,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:));
/**
Returns an array of rendered map features that intersect with the given
@@ -1359,7 +1402,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect NS_SWIFT_NAME(visibleFeatures(in:));
/**
Returns an array of rendered map features that intersect with the given
@@ -1378,7 +1421,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:));
/**
Returns an array of rendered map features that intersect with the given
@@ -1388,9 +1431,9 @@ MGL_EXPORT IB_DESIGNABLE
Each object in the returned array represents a feature rendered by the
current style and provides access to attributes specified by the relevant map
content sources. The returned array includes features loaded by
- `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
- from `MGLRasterSource` objects, or from image, video, or canvas sources, which
- are unsupported by this SDK.
+ `MGLShapeSource` and `MGLVectorTileSource` objects but does not include
+ anything from `MGLRasterTileSource` objects, or from video or canvas sources,
+ which are unsupported by this SDK.
The returned features are drawn by a style layer in the current style. For
example, suppose the current style uses the
@@ -1423,7 +1466,7 @@ MGL_EXPORT IB_DESIGNABLE
Only visible features are returned. To obtain features regardless of
visibility, use the
- `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLVectorTileSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
@note Layer identifiers are not guaranteed to exist across styles or different
@@ -1431,8 +1474,8 @@ MGL_EXPORT IB_DESIGNABLE
style URL to an explicitly versioned style using a convenience method like
`+[MGLStyle outdoorsStyleURLWithVersion:]`, `MGLMapView`’s “Style URL”
inspectable in Interface Builder, or a manually constructed `NSURL`. This
- approach also avoids layer identifer name changes that will occur in the default
- style’s layers over time.
+ approach also avoids layer identifer name changes that will occur in the
+ default style’s layers over time.
@note Layer identifiers are not guaranteed to exist across styles or different
versions of the same style. Applications that use this API must first set
@@ -1450,7 +1493,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:));
#pragma mark Debugging the Map
@@ -1462,11 +1505,11 @@ MGL_EXPORT IB_DESIGNABLE
*/
@property (nonatomic) MGLMapDebugMaskOptions debugMask;
-@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((deprecated("Use -debugMask and -setDebugMask:.")));
+@property (nonatomic, getter=isDebugActive) BOOL debugActive __attribute__((unavailable("Use -debugMask and -setDebugMask:.")));
-- (void)toggleDebug __attribute__((deprecated("Use -setDebugMask:.")));
+- (void)toggleDebug __attribute__((unavailable("Use -setDebugMask:.")));
-- (void)emptyMemoryCache __attribute__((deprecated));
+- (void)emptyMemoryCache __attribute__((unavailable));
@end
diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm
index 0fae5e0394..978547b9c6 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 "MGLVectorTileSource_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"
@@ -65,6 +66,7 @@
#import "MGLStyle_Private.h"
#import "MGLStyleLayer_Private.h"
#import "MGLMapboxEvents.h"
+#import "MMEConstants.h"
#import "MGLSDKUpdateChecker.h"
#import "MGLCompactCalloutView.h"
#import "MGLAnnotationContainerView.h"
@@ -142,6 +144,9 @@ const CGFloat MGLAnnotationImagePaddingForCallout = 1;
const CGSize MGLAnnotationAccessibilityElementMinimumSize = CGSizeMake(10, 10);
+/// Padding to edge of view that an offscreen annotation must have when being brought onscreen (by being selected)
+const UIEdgeInsets MGLMapViewOffscreenAnnotationPadding = UIEdgeInsetsMake(-20.0f, -20.0f, -20.0f, -20.0f);
+
/// An indication that the requested annotation was not found or is nonexistent.
enum { MGLAnnotationTagNotFound = UINT32_MAX };
@@ -189,14 +194,14 @@ public:
@property (nonatomic) GLKView *glView;
@property (nonatomic) UIImageView *glSnapshotView;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *scaleBarConstraints;
+@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *scaleBarConstraints;
@property (nonatomic, readwrite) MGLScaleBar *scaleBar;
@property (nonatomic, readwrite) UIImageView *compassView;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *compassViewConstraints;
+@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *compassViewConstraints;
@property (nonatomic, readwrite) UIImageView *logoView;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *logoViewConstraints;
+@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *logoViewConstraints;
@property (nonatomic, readwrite) UIButton *attributionButton;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(NSLayoutConstraint *) *attributionButtonConstraints;
+@property (nonatomic) NSMutableArray<NSLayoutConstraint *> *attributionButtonConstraints;
@property (nonatomic, readwrite) MGLStyle *style;
@@ -212,7 +217,7 @@ public:
@property (nonatomic) MGLCameraChangeReason cameraChangeReasonBitmask;
/// Mapping from reusable identifiers to annotation images.
-@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
+@property (nonatomic) NSMutableDictionary<NSString *, MGLAnnotationImage *> *annotationImagesByIdentifier;
/// Currently shown popover representing the selected annotation.
@property (nonatomic) UIView<MGLCalloutView> *calloutViewForSelectedAnnotation;
@@ -226,10 +231,11 @@ public:
@property (nonatomic) CGFloat quickZoomStart;
@property (nonatomic, getter=isDormant) BOOL dormant;
@property (nonatomic, readonly, getter=isRotationAllowed) BOOL rotationAllowed;
+@property (nonatomic) BOOL shouldTriggerHapticFeedbackForCompass;
@property (nonatomic) MGLMapViewProxyAccessibilityElement *mapViewProxyAccessibilityElement;
@property (nonatomic) MGLAnnotationContainerView *annotationContainerView;
@property (nonatomic) MGLUserLocation *userLocation;
-@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *) *annotationViewReuseQueueByIdentifier;
+@property (nonatomic) NSMutableDictionary<NSString *, NSMutableArray<MGLAnnotationView *> *> *annotationViewReuseQueueByIdentifier;
@end
@@ -243,8 +249,6 @@ public:
BOOL _opaque;
- NS_MUTABLE_ARRAY_OF(NSURL *) *_bundledStyleURLs;
-
MGLAnnotationTagContextMap _annotationContextsByAnnotationTag;
MGLAnnotationObjectTagMap _annotationTagsByAnnotation;
@@ -269,7 +273,7 @@ public:
CADisplayLink *_displayLink;
BOOL _needsDisplayRefresh;
- NSUInteger _changeDelimiterSuppressionDepth;
+ NSInteger _changeDelimiterSuppressionDepth;
/// Center coordinate of the pinch gesture on the previous iteration of the gesture.
CLLocationCoordinate2D _previousPinchCenterCoordinate;
@@ -283,9 +287,9 @@ public:
BOOL _delegateHasLineWidthsForShapeAnnotations;
MGLCompassDirectionFormatter *_accessibilityCompassFormatter;
- NS_ARRAY_OF(id <MGLFeature>) *_visiblePlaceFeatures;
- NS_ARRAY_OF(id <MGLFeature>) *_visibleRoadFeatures;
- NS_MUTABLE_SET_OF(MGLFeatureAccessibilityElement *) *_featureAccessibilityElements;
+ NSArray<id <MGLFeature>> *_visiblePlaceFeatures;
+ NSArray<id <MGLFeature>> *_visibleRoadFeatures;
+ NSMutableSet<MGLFeatureAccessibilityElement *> *_featureAccessibilityElements;
BOOL _accessibilityValueAnnouncementIsPending;
MGLReachability *_reachability;
@@ -331,12 +335,12 @@ public:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle
++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyle
{
return [NSSet setWithObject:@"styleURL"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL
++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL
{
return [NSSet setWithObjects:@"styleURL__", nil];
}
@@ -462,6 +466,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];
@@ -520,6 +527,8 @@ public:
[_twoFingerTap requireGestureRecognizerToFail:_twoFingerDrag];
[self addGestureRecognizer:_twoFingerTap];
+ _hapticFeedbackEnabled = YES;
+
_decelerationRate = MGLMapViewDecelerationRateNormal;
_quickZoom = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleQuickZoomGesture:)];
@@ -564,7 +573,8 @@ public:
_targetCoordinate = kCLLocationCoordinate2DInvalid;
if ([UIApplication sharedApplication].applicationState != UIApplicationStateBackground) {
- [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}];
+ [MGLMapboxEvents pushTurnstileEvent];
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}];
}
}
@@ -614,18 +624,8 @@ public:
UIGraphicsBeginImageContextWithOptions(scaleImage.size, NO, [UIScreen mainScreen].scale);
[scaleImage drawInRect:{ CGPointZero, scaleImage.size }];
- CGFloat northSize = 11;
- UIFont *northFont;
- if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)])
- {
- northFont = [UIFont systemFontOfSize:northSize weight:UIFontWeightUltraLight];
- }
- else
- {
- northFont = [UIFont systemFontOfSize:northSize];
- }
NSAttributedString *north = [[NSAttributedString alloc] initWithString:NSLocalizedStringWithDefaultValue(@"COMPASS_NORTH", nil, nil, @"N", @"Compass abbreviation for north") attributes:@{
- NSFontAttributeName: northFont,
+ NSFontAttributeName: [UIFont systemFontOfSize:11 weight:UIFontWeightUltraLight],
NSForegroundColorAttributeName: [UIColor whiteColor],
}];
CGRect stringRect = CGRectMake((scaleImage.size.width - north.size.width) / 2,
@@ -713,7 +713,7 @@ public:
{
MGLAssertIsMainThread();
- _rendererFrontend->onLowMemory();
+ _rendererFrontend->reduceMemoryUse();
}
#pragma mark - Layout -
@@ -966,8 +966,8 @@ public:
- (void)layoutSubviews
{
// Calling this here instead of in the scale bar itself because if this is done in the
- // scale bar instance, it triggers a call to this this `layoutSubviews` method that
- // calls `_mbglMap->setSize()` just below that triggers rendering update which triggers
+ // scale bar instance, it triggers a call to this `layoutSubviews` method that calls
+ // `_mbglMap->setSize()` just below that triggers rendering update which triggers
// another scale bar update which causes a rendering update loop and a major performace
// degradation. The only time the scale bar's intrinsic content size _must_ invalidated
// is here as a reaction to this object's view dimension changes.
@@ -1155,6 +1155,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)
{
@@ -1209,7 +1216,8 @@ public:
[self validateLocationServices];
- [MGLMapboxEvents pushEvent:MGLEventTypeMapLoad withAttributes:@{}];
+ [MGLMapboxEvents pushTurnstileEvent];
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapLoad withAttributes:@{}];
}
}
@@ -1255,7 +1263,7 @@ public:
}
}
-- (void)touchesBegan:(__unused NS_SET_OF(UITouch *) *)touches withEvent:(__unused UIEvent *)event
+- (void)touchesBegan:(__unused NSSet<UITouch *> *)touches withEvent:(__unused UIEvent *)event
{
_changeDelimiterSuppressionDepth = 0;
_mbglMap->setGestureInProgress(false);
@@ -1321,7 +1329,7 @@ public:
if (pan.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePanStart forRecognizer:pan];
+ [self trackGestureEvent:MMEEventGesturePanStart forRecognizer:pan];
self.userTrackingMode = MGLUserTrackingModeNone;
@@ -1369,10 +1377,10 @@ public:
CLLocationCoordinate2D panCoordinate = [self convertPoint:pointInView toCoordinateFromView:pan.view];
int zoom = round([self zoomLevel]);
- [MGLMapboxEvents pushEvent:MGLEventTypeMapDragEnd withAttributes:@{
- MGLEventKeyLatitude: @(panCoordinate.latitude),
- MGLEventKeyLongitude: @(panCoordinate.longitude),
- MGLEventKeyZoomLevel: @(zoom)
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapDragEnd withAttributes:@{
+ MMEEventKeyLatitude: @(panCoordinate.latitude),
+ MMEEventKeyLongitude: @(panCoordinate.longitude),
+ MMEEventKeyZoomLevel: @(zoom)
}];
}
@@ -1391,7 +1399,7 @@ public:
if (pinch.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePinchStart forRecognizer:pinch];
+ [self trackGestureEvent:MMEEventGesturePinchStart forRecognizer:pinch];
self.scale = powf(2, _mbglMap->getZoom());
@@ -1492,7 +1500,7 @@ public:
if (rotate.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureRotateStart forRecognizer:rotate];
+ [self trackGestureEvent:MMEEventGestureRotateStart forRecognizer:rotate];
self.angle = MGLRadiansFromDegrees(_mbglMap->getBearing()) * -1;
@@ -1501,6 +1509,8 @@ public:
self.userTrackingMode = MGLUserTrackingModeFollow;
}
+ self.shouldTriggerHapticFeedbackForCompass = NO;
+
[self notifyGestureDidBegin];
}
else if (rotate.state == UIGestureRecognizerStateChanged)
@@ -1523,6 +1533,22 @@ public:
}
[self cameraIsChanging];
+
+ // Trigger a light haptic feedback event when the user rotates to due north.
+ if (@available(iOS 10.0, *))
+ {
+ if (self.isHapticFeedbackEnabled && fabs(newDegrees) <= 1 && self.shouldTriggerHapticFeedbackForCompass)
+ {
+ UIImpactFeedbackGenerator *hapticFeedback = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
+ [hapticFeedback impactOccurred];
+
+ self.shouldTriggerHapticFeedbackForCompass = NO;
+ }
+ else if (fabs(newDegrees) > 1)
+ {
+ self.shouldTriggerHapticFeedbackForCompass = YES;
+ }
+ }
}
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
{
@@ -1564,7 +1590,7 @@ public:
{
return;
}
- [self trackGestureEvent:MGLEventGestureSingleTap forRecognizer:singleTap];
+ [self trackGestureEvent:MMEEventGestureSingleTap forRecognizer:singleTap];
if (self.mapViewProxyAccessibilityElement.accessibilityElementIsFocused)
{
@@ -1590,7 +1616,7 @@ public:
{
CGPoint calloutPoint = [singleTap locationInView:self];
CGRect positionRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:calloutPoint];
- [self selectAnnotation:annotation animated:YES calloutPositioningRect:positionRect];
+ [self selectAnnotation:annotation moveOnscreen:YES animateSelection:YES calloutPositioningRect:positionRect];
}
else if (self.selectedAnnotation)
{
@@ -1686,7 +1712,7 @@ public:
if ([self _shouldChangeFromCamera:oldCamera toCamera:toCamera])
{
- [self trackGestureEvent:MGLEventGestureDoubleTap forRecognizer:doubleTap];
+ [self trackGestureEvent:MMEEventGestureDoubleTap forRecognizer:doubleTap];
mbgl::ScreenCoordinate center(gesturePoint.x, gesturePoint.y);
_mbglMap->setZoom(newZoom, center, MGLDurationFromTimeInterval(MGLAnimationDuration));
@@ -1717,7 +1743,7 @@ public:
if (twoFingerTap.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
+ [self trackGestureEvent:MMEEventGestureTwoFingerSingleTap forRecognizer:twoFingerTap];
[self notifyGestureDidBegin];
}
@@ -1756,7 +1782,7 @@ public:
if (quickZoom.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGestureQuickZoom forRecognizer:quickZoom];
+ [self trackGestureEvent:MMEEventGestureQuickZoom forRecognizer:quickZoom];
self.scale = powf(2, _mbglMap->getZoom());
@@ -1801,7 +1827,7 @@ public:
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
- [self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
+ [self trackGestureEvent:MMEEventGesturePitchStart forRecognizer:twoFingerDrag];
[self notifyGestureDidBegin];
}
@@ -2006,23 +2032,38 @@ public:
CLLocationCoordinate2D gestureCoordinate = [self convertPoint:pointInView toCoordinateFromView:recognizer.view];
int zoom = round([self zoomLevel]);
- [MGLMapboxEvents pushEvent:MGLEventTypeMapTap withAttributes:@{
- MGLEventKeyLatitude: @(gestureCoordinate.latitude),
- MGLEventKeyLongitude: @(gestureCoordinate.longitude),
- MGLEventKeyZoomLevel: @(zoom),
- MGLEventKeyGestureID: gestureID
+ [MGLMapboxEvents pushEvent:MMEEventTypeMapTap withAttributes:@{
+ MMEEventKeyLatitude: @(gestureCoordinate.latitude),
+ MMEEventKeyLongitude: @(gestureCoordinate.longitude),
+ MMEEventKeyZoomLevel: @(zoom),
+ MMEEventKeyGestureID: gestureID
}];
}
#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)
@@ -2187,22 +2228,22 @@ public:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomEnabled
++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomEnabled
{
return [NSSet setWithObject:@"allowsZooming"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingScrollEnabled
++ (NSSet<NSString *> *)keyPathsForValuesAffectingScrollEnabled
{
return [NSSet setWithObject:@"allowsScrolling"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingRotateEnabled
++ (NSSet<NSString *> *)keyPathsForValuesAffectingRotateEnabled
{
return [NSSet setWithObject:@"allowsRotating"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitchEnabled
++ (NSSet<NSString *> *)keyPathsForValuesAffectingPitchEnabled
{
return [NSSet setWithObject:@"allowsTilting"];
}
@@ -2267,16 +2308,6 @@ public:
MGLMapDebugCollisionBoxesMask) : 0;
}
-- (BOOL)isDebugActive
-{
- return self.debugMask;
-}
-
-- (void)toggleDebug
-{
- self.debugActive = !self.debugActive;
-}
-
- (void)resetNorth
{
[self resetNorthAnimated:YES];
@@ -2299,11 +2330,6 @@ public:
heading:heading];
}
-- (void)emptyMemoryCache
-{
- _rendererFrontend->onLowMemory();
-}
-
- (void)setZoomEnabled:(BOOL)zoomEnabled
{
_zoomEnabled = zoomEnabled;
@@ -2331,6 +2357,17 @@ public:
self.twoFingerDrag.enabled = pitchEnabled;
}
+- (void)setShowsScale:(BOOL)showsScale
+{
+ _showsScale = showsScale;
+ self.scaleBar.hidden = !showsScale;
+
+ if (showsScale)
+ {
+ [self updateScaleBar];
+ }
+}
+
#pragma mark - Accessibility -
- (NSString *)accessibilityValue
@@ -2372,7 +2409,7 @@ public:
return value;
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visiblePlaceFeatures
+- (NSArray<id <MGLFeature>> *)visiblePlaceFeatures
{
if (!_visiblePlaceFeatures)
{
@@ -2382,7 +2419,7 @@ public:
return _visiblePlaceFeatures;
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleRoadFeatures
+- (NSArray<id <MGLFeature>> *)visibleRoadFeatures
{
if (!_visibleRoadFeatures)
{
@@ -2860,7 +2897,7 @@ public:
#pragma mark - Geography -
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCenterCoordinate
{
return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil];
}
@@ -2951,7 +2988,7 @@ public:
_mbglMap->easeTo(cameraOptions, animationOptions);
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel
++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomLevel
{
return [NSSet setWithObject:@"camera"];
}
@@ -3120,7 +3157,7 @@ public:
[self didChangeValueForKey:@"visibleCoordinateBounds"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection
++ (NSSet<NSString *> *)keyPathsForValuesAffectingDirection
{
return [NSSet setWithObject:@"camera"];
}
@@ -3170,12 +3207,12 @@ public:
[self setDirection:direction animated:NO];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitch
++ (NSSet<NSString *> *)keyPathsForValuesAffectingPitch
{
return [NSSet setWithObject:@"camera"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCamera
{
return [NSSet setWithObjects:@"longitude", @"latitude", @"centerCoordinate", @"zoomLevel", @"direction", nil];
}
@@ -3413,36 +3450,24 @@ public:
/// bounding box.
- (mbgl::LatLngBounds)convertRect:(CGRect)rect toLatLngBoundsFromView:(nullable UIView *)view
{
- mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
- bounds.extend([self convertPoint:rect.origin toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMinY(rect) } toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ CGRectGetMinX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view]);
-
- // The world is wrapping if a point just outside the bounds is also within
- // the rect.
- mbgl::LatLng outsideLatLng;
- if (bounds.west() > -180)
- {
- outsideLatLng = {
- (bounds.south() + bounds.north()) / 2,
- bounds.west() - 1,
- };
- }
- else if (bounds.east() < 180)
- {
- outsideLatLng = {
- (bounds.south() + bounds.north()) / 2,
- bounds.east() + 1,
- };
- }
-
- // If the world is wrapping, extend the bounds to cover all longitudes.
- if (CGRectContainsPoint(rect, [self convertLatLng:outsideLatLng toPointToView:view]))
- {
- bounds.extend(mbgl::LatLng(bounds.south(), -180));
- bounds.extend(mbgl::LatLng(bounds.south(), 180));
- }
+ auto bounds = mbgl::LatLngBounds::empty();
+ auto topLeft = [self convertPoint:{ CGRectGetMinX(rect), CGRectGetMinY(rect) } toLatLngFromView:view];
+ auto topRight = [self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMinY(rect) } toLatLngFromView:view];
+ auto bottomRight = [self convertPoint:{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view];
+ auto bottomLeft = [self convertPoint:{ CGRectGetMinX(rect), CGRectGetMaxY(rect) } toLatLngFromView:view];
+
+ // If the bounds straddles the antimeridian, unwrap it so that one side
+ // extends beyond ±180° longitude.
+ auto center = [self convertPoint:{ CGRectGetMidX(rect), CGRectGetMidY(rect) } toLatLngFromView:view];
+ topLeft.unwrapForShortestPath(center);
+ topRight.unwrapForShortestPath(center);
+ bottomRight.unwrapForShortestPath(center);
+ bottomLeft.unwrapForShortestPath(center);
+
+ bounds.extend(topLeft);
+ bounds.extend(topRight);
+ bounds.extend(bottomRight);
+ bounds.extend(bottomLeft);
return bounds;
}
@@ -3452,11 +3477,6 @@ public:
return mbgl::Projection::getMetersPerPixelAtLatitude(latitude, self.zoomLevel);
}
-- (CLLocationDistance)metersPerPixelAtLatitude:(CLLocationDegrees)latitude
-{
- return [self metersPerPointAtLatitude:latitude];
-}
-
#pragma mark - Camera Change Reason -
- (void)resetCameraChangeReason
@@ -3464,73 +3484,9 @@ public:
self.cameraChangeReasonBitmask = MGLCameraChangeReasonNone;
}
-#pragma mark - Styling -
-
-- (NS_ARRAY_OF(NSURL *) *)bundledStyleURLs
-{
- if ( ! _bundledStyleURLs)
- {
- _bundledStyleURLs = [NSMutableArray array];
- for (NSUInteger i = 0; i < mbgl::util::default_styles::numOrderedStyles; i++)
- {
- NSURL *styleURL = [NSURL URLWithString:@(mbgl::util::default_styles::orderedStyles[i].url)];
- [_bundledStyleURLs addObject:styleURL];
- }
- }
-
- return [NSArray arrayWithArray:_bundledStyleURLs];
-}
-
-- (nullable NSString *)styleID
-{
- [NSException raise:@"Method unavailable" format:
- @"%s has been replaced by -[MGLMapView styleURL].",
- __PRETTY_FUNCTION__];
- return nil;
-}
-
-- (void)setStyleID:(nullable NSString *)styleID
-{
- [NSException raise:@"Method unavailable" format:
- @"%s has been replaced by -[MGLMapView setStyleURL:].\n\n"
- @"If you previously set this style ID in a storyboard inspectable, select the MGLMapView in Interface Builder and delete the “styleID” entry from the User Defined Runtime Attributes section of the Identity inspector. "
- @"Then go to the Attributes inspector and enter “mapbox://styles/%@” into the “Style URL” field.",
- __PRETTY_FUNCTION__, styleID];
-}
-
-- (NS_ARRAY_OF(NSString *) *)styleClasses
-{
- return [self.style styleClasses];
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses
-{
- [self setStyleClasses:appliedClasses transitionDuration:0];
-}
-
-- (void)setStyleClasses:(NS_ARRAY_OF(NSString *) *)appliedClasses transitionDuration:(NSTimeInterval)transitionDuration
-{
- [self.style setStyleClasses:appliedClasses transitionDuration:transitionDuration];
-}
-
-- (BOOL)hasStyleClass:(NSString *)styleClass
-{
- return [self.style hasStyleClass:styleClass];
-}
-
-- (void)addStyleClass:(NSString *)styleClass
-{
- [self.style addStyleClass:styleClass];
-}
-
-- (void)removeStyleClass:(NSString *)styleClass
-{
- [self.style removeStyleClass:styleClass];
-}
-
#pragma mark - Annotations -
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)annotations
+- (nullable NSArray<id <MGLAnnotation>> *)annotations
{
if (_annotationContextsByAnnotationTag.empty())
{
@@ -3554,12 +3510,12 @@ public:
return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()];
}
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotations
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotations
{
return [self visibleAnnotationsInRect:self.bounds];
}
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect
{
if (_annotationContextsByAnnotationTag.empty())
{
@@ -3633,7 +3589,7 @@ public:
[self addAnnotations:@[ annotation ]];
}
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations
+- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations
{
if ( ! annotations) return;
[self willChangeValueForKey:@"annotations"];
@@ -3774,7 +3730,7 @@ public:
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
}
-- (void)updateAnnotationContainerViewWithAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews
+- (void)updateAnnotationContainerViewWithAnnotationViews:(NSArray<MGLAnnotationView *> *)annotationViews
{
if (annotationViews.count == 0) return;
@@ -3929,7 +3885,7 @@ public:
[self removeAnnotations:@[ annotation ]];
}
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations
+- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations
{
if ( ! annotations) return;
@@ -3990,11 +3946,11 @@ public:
}
}
-- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+- (nonnull NSArray<id <MGLOverlay>> *)overlays
{
if (self.annotations == nil) { return @[]; }
- NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array];
+ NSMutableArray<id <MGLOverlay>> *mutableOverlays = [NSMutableArray array];
[self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) {
if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
@@ -4011,7 +3967,7 @@ public:
[self addOverlays:@[ overlay ]];
}
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays
{
#if DEBUG
for (id <MGLOverlay> overlay in overlays)
@@ -4028,7 +3984,7 @@ public:
[self removeOverlays:@[ overlay ]];
}
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays
{
#if DEBUG
for (id <MGLOverlay> overlay in overlays)
@@ -4148,51 +4104,48 @@ public:
MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound;
if (nearbyAnnotations.size())
{
- // The annotation tags need to be stable in order to compare them with
- // the remembered tags.
- std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end());
-
+ // The first selection in the cycle should be the one nearest to the
+ // tap. Also the annotation tags need to be stable in order to compare them with
+ // the remembered tags _annotationsNearbyLastTap.
+ CLLocationCoordinate2D currentCoordinate = [self convertPoint:point toCoordinateFromView:self];
+ std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
+ CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
+ CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
+ CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude,
+ coordinateA.longitude - currentCoordinate.longitude);
+ CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude,
+ coordinateB.longitude - currentCoordinate.longitude);
+ return deltaA < deltaB;
+ });
+
if (nearbyAnnotations == _annotationsNearbyLastTap)
{
- // The first selection in the cycle should be the one nearest to the
- // tap.
- CLLocationCoordinate2D currentCoordinate = [self convertPoint:point toCoordinateFromView:self];
- std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
- CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
- CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
- CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude,
- coordinateA.longitude - currentCoordinate.longitude);
- CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude,
- coordinateB.longitude - currentCoordinate.longitude);
- return deltaA < deltaB;
- });
-
// The last time we persisted a set of annotations, we had the same
// set of annotations as we do now. Cycle through them.
if (_selectedAnnotationTag == MGLAnnotationTagNotFound
- || _selectedAnnotationTag == _annotationsNearbyLastTap.back())
+ || _selectedAnnotationTag == nearbyAnnotations.back())
{
// Either no annotation is selected or the last annotation in
// the set was selected. Wrap around to the first annotation in
// the set.
- hitAnnotationTag = _annotationsNearbyLastTap.front();
+ hitAnnotationTag = nearbyAnnotations.front();
}
else
{
- auto result = std::find(_annotationsNearbyLastTap.begin(),
- _annotationsNearbyLastTap.end(),
+ auto result = std::find(nearbyAnnotations.begin(),
+ nearbyAnnotations.end(),
_selectedAnnotationTag);
- if (result == _annotationsNearbyLastTap.end())
+ if (result == nearbyAnnotations.end())
{
// An annotation from this set hasn’t been selected before.
// Select the first (nearest) one.
- hitAnnotationTag = _annotationsNearbyLastTap.front();
+ hitAnnotationTag = nearbyAnnotations.front();
}
else
{
// Step to the next annotation in the set.
- auto distance = std::distance(_annotationsNearbyLastTap.begin(), result);
- hitAnnotationTag = _annotationsNearbyLastTap[distance + 1];
+ auto distance = std::distance(nearbyAnnotations.begin(), result);
+ hitAnnotationTag = nearbyAnnotations[distance + 1];
}
}
}
@@ -4204,7 +4157,7 @@ public:
{
_annotationsNearbyLastTap = nearbyAnnotations;
}
-
+
// Choose the first nearby annotation.
if (nearbyAnnotations.size())
{
@@ -4233,6 +4186,12 @@ public:
});
}
+
+- (BOOL)isBringingAnnotationOnscreenSupportedForAnnotation:(id<MGLAnnotation>)annotation animated:(BOOL)animated {
+ // Consider delegating
+ return animated && [annotation isKindOfClass:[MGLPointAnnotation class]];
+}
+
- (id <MGLAnnotation>)selectedAnnotation
{
if (_userLocationAnnotationIsSelected)
@@ -4257,13 +4216,13 @@ public:
[self didChangeValueForKey:@"selectedAnnotations"];
}
-- (NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations
+- (NSArray<id <MGLAnnotation>> *)selectedAnnotations
{
id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
return (selectedAnnotation ? @[ selectedAnnotation ] : @[]);
}
-- (void)setSelectedAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations
+- (void)setSelectedAnnotations:(NSArray<id <MGLAnnotation>> *)selectedAnnotations
{
if ( ! selectedAnnotations.count) return;
@@ -4273,20 +4232,16 @@ public:
if ([firstAnnotation isKindOfClass:[MGLMultiPoint class]]) return;
- // Select the annotation if it’s visible.
- if (MGLCoordinateInCoordinateBounds(firstAnnotation.coordinate, self.visibleCoordinateBounds))
- {
- [self selectAnnotation:firstAnnotation animated:NO];
- }
+ [self selectAnnotation:firstAnnotation animated:YES];
}
- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated
{
CGRect positioningRect = [self positioningRectForAnnotation:annotation defaultCalloutPoint:CGPointZero];
- [self selectAnnotation:annotation animated:animated calloutPositioningRect:positioningRect];
+ [self selectAnnotation:annotation moveOnscreen:animated animateSelection:YES calloutPositioningRect:positioningRect];
}
-- (void)selectAnnotation:(id <MGLAnnotation>)annotation animated:(BOOL)animated calloutPositioningRect:(CGRect)calloutPositioningRect
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation moveOnscreen:(BOOL)moveOnscreen animateSelection:(BOOL)animateSelection calloutPositioningRect:(CGRect)calloutPositioningRect
{
if ( ! annotation) return;
@@ -4310,22 +4265,34 @@ public:
MGLAnnotationContext &annotationContext = _annotationContextsByAnnotationTag.at(annotationTag);
annotationView = annotationContext.annotationView;
if (annotationView && annotationView.enabled) {
- // Annotations represented by views use the view frame as the positioning rect.
- calloutPositioningRect = annotationView.frame;
- [annotationView.superview bringSubviewToFront:annotationView];
- [annotationView setSelected:YES animated:animated];
+ // Annotations represented by views use the view frame as the positioning rect.
+ calloutPositioningRect = annotationView.frame;
+ [annotationView.superview bringSubviewToFront:annotationView];
+
+ [annotationView setSelected:YES animated:animateSelection];
}
}
self.selectedAnnotation = annotation;
+ // Determine if we're allowed to move this offscreen annotation on screen, even though we've asked it to
+ if (moveOnscreen) {
+ moveOnscreen = [self isBringingAnnotationOnscreenSupportedForAnnotation:annotation animated:animateSelection];
+ }
+
+ CGRect expandedPositioningRect = UIEdgeInsetsInsetRect(calloutPositioningRect, MGLMapViewOffscreenAnnotationPadding);
+
+ // Used for callout positioning, and moving offscreen annotations onscreen.
+ CGRect constrainedRect = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
+
+ UIView <MGLCalloutView> *calloutView = nil;
+
if ([annotation respondsToSelector:@selector(title)] &&
annotation.title &&
[self.delegate respondsToSelector:@selector(mapView:annotationCanShowCallout:)] &&
[self.delegate mapView:self annotationCanShowCallout:annotation])
{
// build the callout
- UIView <MGLCalloutView> *calloutView;
if ([self.delegate respondsToSelector:@selector(mapView:calloutViewForAnnotation:)])
{
id providedCalloutView = [self.delegate mapView:self calloutViewForAnnotation:annotation];
@@ -4383,13 +4350,51 @@ public:
// set annotation delegate to handle taps on the callout view
calloutView.delegate = self;
- // present popup
- [calloutView presentCalloutFromRect:calloutPositioningRect
- inView:self.glView
- constrainedToView:self.glView
- animated:animated];
+ // If the callout view provides inset (outset) information, we can use it to expand our positioning
+ // rect, which we then use to help move the annotation on-screen if want need to.
+ if (moveOnscreen && [calloutView respondsToSelector:@selector(marginInsetsHintForPresentationFromRect:)]) {
+ UIEdgeInsets margins = [calloutView marginInsetsHintForPresentationFromRect:calloutPositioningRect];
+ expandedPositioningRect = UIEdgeInsetsInsetRect(expandedPositioningRect, margins);
+ }
+ }
+
+ if (moveOnscreen)
+ {
+ moveOnscreen = NO;
+
+ // Need to consider the content insets.
+ CGRect bounds = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
+
+ // Any one of these cases should trigger a move onscreen
+ if (CGRectGetMinX(calloutPositioningRect) < CGRectGetMinX(bounds))
+ {
+ constrainedRect.origin.x = expandedPositioningRect.origin.x;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxX(calloutPositioningRect) > CGRectGetMaxX(bounds))
+ {
+ constrainedRect.origin.x = CGRectGetMaxX(expandedPositioningRect) - constrainedRect.size.width;
+ moveOnscreen = YES;
+ }
+
+ if (CGRectGetMinY(calloutPositioningRect) < CGRectGetMinY(bounds))
+ {
+ constrainedRect.origin.y = expandedPositioningRect.origin.y;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxY(calloutPositioningRect) > CGRectGetMaxY(bounds))
+ {
+ constrainedRect.origin.y = CGRectGetMaxY(expandedPositioningRect) - constrainedRect.size.height;
+ moveOnscreen = YES;
+ }
}
+ // Remember, calloutView can be nil here.
+ [calloutView presentCalloutFromRect:calloutPositioningRect
+ inView:self.glView
+ constrainedToRect:constrainedRect
+ animated:animateSelection];
+
// notify delegate
if ([self.delegate respondsToSelector:@selector(mapView:didSelectAnnotation:)])
{
@@ -4400,6 +4405,13 @@ public:
{
[self.delegate mapView:self didSelectAnnotationView:annotationView];
}
+
+ if (moveOnscreen)
+ {
+ CGPoint center = CGPointMake(CGRectGetMidX(constrainedRect), CGRectGetMidY(constrainedRect));
+ CLLocationCoordinate2D centerCoord = [self convertPoint:center toCoordinateFromView:self];
+ [self setCenterCoordinate:centerCoord animated:animateSelection];
+ }
}
- (MGLCompactCalloutView *)calloutViewForAnnotation:(id <MGLAnnotation>)annotation
@@ -4556,7 +4568,7 @@ public:
completion:NULL];
}
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated
{
CGFloat maximumPadding = 100;
CGFloat yPadding = (self.frame.size.height / 5 <= maximumPadding) ? (self.frame.size.height / 5) : maximumPadding;
@@ -4567,7 +4579,7 @@ public:
[self showAnnotations:annotations edgePadding:edgeInsets animated:animated];
}
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(UIEdgeInsets)insets animated:(BOOL)animated
{
if ( ! annotations || ! annotations.count) return;
@@ -4590,6 +4602,7 @@ public:
animated:animated];
}
+
#pragma mark Annotation Image Delegate
- (void)annotationImageNeedsRedisplay:(MGLAnnotationImage *)annotationImage
@@ -4599,11 +4612,6 @@ public:
NSString *fallbackReuseIdentifier = MGLDefaultStyleMarkerSymbolName;
NSString *fallbackIconIdentifier = [MGLAnnotationSpritePrefix stringByAppendingString:fallbackReuseIdentifier];
- // Remove the old icon from the style.
- if ( ! [iconIdentifier isEqualToString:fallbackIconIdentifier]) {
- _mbglMap->removeAnnotationImage(iconIdentifier.UTF8String);
- }
-
if (annotationImage.image)
{
// Add the new icon to the style.
@@ -4720,12 +4728,8 @@ public:
userLocationAnnotationView = (MGLUserLocationAnnotationView *)[self.delegate mapView:self viewForAnnotation:self.userLocation];
if (userLocationAnnotationView && ! [userLocationAnnotationView isKindOfClass:MGLUserLocationAnnotationView.class])
{
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSLog(@"Ignoring user location annotation view with type %@. User location annotation view must be a kind of MGLUserLocationAnnotationView. This warning is only shown once and will become an error in a future version.", NSStringFromClass(userLocationAnnotationView.class));
- });
-
- userLocationAnnotationView = nil;
+ [NSException raise:@"MGLUserLocationAnnotationTypeException"
+ format:@"User location annotation view must be a kind of MGLUserLocationAnnotationView. %@", userLocationAnnotationView.debugDescription];
}
}
@@ -4763,7 +4767,7 @@ public:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingUserLocation
++ (NSSet<NSString *> *)keyPathsForValuesAffectingUserLocation
{
return [NSSet setWithObject:@"userLocationAnnotationView"];
}
@@ -5254,16 +5258,16 @@ public:
#pragma mark Data
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point
{
return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers {
return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate
{
mbgl::ScreenCoordinate screenCoordinate = { point.x, point.y };
@@ -5288,15 +5292,15 @@ public:
return MGLFeaturesFromMBGLFeatures(features);
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect {
return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers {
return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
mbgl::ScreenBox screenBox = {
{ CGRectGetMinX(rect), CGRectGetMinY(rect) },
{ CGRectGetMaxX(rect), CGRectGetMaxY(rect) },
@@ -5429,10 +5433,7 @@ public:
}
[self updateCompass];
-
- if (!self.scaleBar.hidden) {
- [(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
- }
+ [self updateScaleBar];
if ([self.delegate respondsToSelector:@selector(mapView:regionIsChangingWithReason:)])
{
@@ -5450,6 +5451,7 @@ public:
}
[self updateCompass];
+ [self updateScaleBar];
if ( ! [self isSuppressingChangeDelimiters])
{
@@ -5894,6 +5896,17 @@ public:
}
}
+- (void)updateScaleBar
+{
+ // Use the `hidden` property (instead of `self.showsScale`) so that we don't
+ // break developers who still rely on the <4.0.0 approach of directly
+ // setting this property.
+ if ( ! self.scaleBar.hidden)
+ {
+ [(MGLScaleBar *)self.scaleBar setMetersPerPoint:[self metersPerPointAtLatitude:self.centerCoordinate.latitude]];
+ }
+}
+
+ (UIImage *)resourceImageNamed:(NSString *)imageName
{
UIImage *image = [UIImage imageNamed:imageName
@@ -6017,7 +6030,7 @@ public:
views:views]];
}
-- (NS_MUTABLE_ARRAY_OF(MGLAnnotationView *) *)annotationViewReuseQueueForIdentifier:(NSString *)identifier {
+- (NSMutableArray<MGLAnnotationView *> *)annotationViewReuseQueueForIdentifier:(NSString *)identifier {
if (!_annotationViewReuseQueueByIdentifier[identifier])
{
_annotationViewReuseQueueByIdentifier[identifier] = [NSMutableArray array];
@@ -6128,7 +6141,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.");
@@ -6174,7 +6187,7 @@ private:
@implementation MGLMapView (IBAdditions)
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__
++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL__
{
return [NSSet setWithObject:@"styleURL"];
}
@@ -6197,7 +6210,7 @@ private:
self.styleURL = url;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude
++ (NSSet<NSString *> *)keyPathsForValuesAffectingLatitude
{
return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil];
}
@@ -6223,7 +6236,7 @@ private:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude
++ (NSSet<NSString *> *)keyPathsForValuesAffectingLongitude
{
return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil];
}
@@ -6249,7 +6262,7 @@ private:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsZooming
{
return [NSSet setWithObject:@"zoomEnabled"];
}
@@ -6264,7 +6277,7 @@ private:
self.zoomEnabled = allowsZooming;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsScrolling
{
return [NSSet setWithObject:@"scrollEnabled"];
}
@@ -6279,7 +6292,7 @@ private:
self.scrollEnabled = allowsScrolling;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsRotating
{
return [NSSet setWithObject:@"rotateEnabled"];
}
@@ -6294,7 +6307,7 @@ private:
self.rotateEnabled = allowsRotating;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsTilting
{
return [NSSet setWithObject:@"pitchEnabled"];
}
@@ -6309,7 +6322,7 @@ private:
self.pitchEnabled = allowsTilting;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingShowsHeading
++ (NSSet<NSString *> *)keyPathsForValuesAffectingShowsHeading
{
return [NSSet setWithObject:@"showsUserHeadingIndicator"];
}
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 0368d8413c..201e3db84b 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -428,7 +428,7 @@ NS_ASSUME_NONNULL_BEGIN
@param annotationViews An array of `MGLAnnotationView` objects representing the
views that were added.
*/
-- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NS_ARRAY_OF(MGLAnnotationView *) *)annotationViews;
+- (void)mapView:(MGLMapView *)mapView didAddAnnotationViews:(NSArray<MGLAnnotationView *> *)annotationViews;
#pragma mark Selecting Annotations
diff --git a/platform/ios/src/MGLMapboxEvents.h b/platform/ios/src/MGLMapboxEvents.h
index 98f59ffd3f..cbac578798 100644
--- a/platform/ios/src/MGLMapboxEvents.h
+++ b/platform/ios/src/MGLMapboxEvents.h
@@ -1,44 +1,17 @@
#import <Foundation/Foundation.h>
-
-#import "MGLTypes.h"
+#import "MMEEventsManager.h"
NS_ASSUME_NONNULL_BEGIN
-// Event types
-extern NSString *const MGLEventTypeAppUserTurnstile;
-extern NSString *const MGLEventTypeMapLoad;
-extern NSString *const MGLEventTypeMapTap;
-extern NSString *const MGLEventTypeMapDragEnd;
-extern NSString *const MGLEventTypeLocation;
-
-// Event keys
-extern NSString *const MGLEventKeyLatitude;
-extern NSString *const MGLEventKeyLongitude;
-extern NSString *const MGLEventKeyZoomLevel;
-extern NSString *const MGLEventKeyGestureID;
-
-// Gestures
-extern NSString *const MGLEventGestureSingleTap;
-extern NSString *const MGLEventGestureDoubleTap;
-extern NSString *const MGLEventGestureTwoFingerSingleTap;
-extern NSString *const MGLEventGestureQuickZoom;
-extern NSString *const MGLEventGesturePanStart;
-extern NSString *const MGLEventGesturePinchStart;
-extern NSString *const MGLEventGestureRotateStart;
-extern NSString *const MGLEventGesturePitchStart;
-
-typedef NS_DICTIONARY_OF(NSString *, id) MGLMapboxEventAttributes;
-typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes;
-
@interface MGLMapboxEvents : NSObject
-+ (nullable instancetype)sharedManager;
++ (nullable instancetype)sharedInstance;
-// You must call these methods from the main thread.
-//
-+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary;
-+ (void)ensureMetricsOptoutExists;
++ (void)setupWithAccessToken:(NSString *)accessToken;
++ (void)pushTurnstileEvent;
++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary;
+ (void)flush;
++ (void)ensureMetricsOptoutExists;
@end
diff --git a/platform/ios/src/MGLMapboxEvents.m b/platform/ios/src/MGLMapboxEvents.m
index 273af5b3bc..05f291d8a0 100644
--- a/platform/ios/src/MGLMapboxEvents.m
+++ b/platform/ios/src/MGLMapboxEvents.m
@@ -1,646 +1,166 @@
#import "MGLMapboxEvents.h"
-#import <UIKit/UIKit.h>
-#import <CoreLocation/CoreLocation.h>
-#import "MGLAccountManager.h"
+#import "NSBundle+MGLAdditions.h"
#import "NSProcessInfo+MGLAdditions.h"
-#import "NSException+MGLAdditions.h"
-#import "MGLAPIClient.h"
-#import "MGLLocationManager.h"
-#import "MGLTelemetryConfig.h"
-#include <mbgl/storage/reachability.h>
-#include <sys/sysctl.h>
+static NSString * const MGLAPIClientUserAgentBase = @"mapbox-maps-ios";
+static NSString * const MGLMapboxAccountType = @"MGLMapboxAccountType";
+static NSString * const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled";
+static NSString * const MGLMapboxMetricsDebugLoggingEnabled = @"MGLMapboxMetricsDebugLoggingEnabled";
+static NSString * const MGLTelemetryAccessToken = @"MGLTelemetryAccessToken";
+static NSString * const MGLTelemetryBaseURL = @"MGLTelemetryBaseURL";
-// Event types
-NSString *const MGLEventTypeAppUserTurnstile = @"appUserTurnstile";
-NSString *const MGLEventTypeMapLoad = @"map.load";
-NSString *const MGLEventTypeMapTap = @"map.click";
-NSString *const MGLEventTypeMapDragEnd = @"map.dragend";
-NSString *const MGLEventTypeLocation = @"location";
-NSString *const MGLEventTypeLocalDebug = @"debug";
+@interface MGLMapboxEvents ()
-// Gestures
-NSString *const MGLEventGestureSingleTap = @"SingleTap";
-NSString *const MGLEventGestureDoubleTap = @"DoubleTap";
-NSString *const MGLEventGestureTwoFingerSingleTap = @"TwoFingerTap";
-NSString *const MGLEventGestureQuickZoom = @"QuickZoom";
-NSString *const MGLEventGesturePanStart = @"Pan";
-NSString *const MGLEventGesturePinchStart = @"Pinch";
-NSString *const MGLEventGestureRotateStart = @"Rotation";
-NSString *const MGLEventGesturePitchStart = @"Pitch";
-
-// Event keys
-NSString *const MGLEventKeyLatitude = @"lat";
-NSString *const MGLEventKeyLongitude = @"lng";
-NSString *const MGLEventKeyZoomLevel = @"zoom";
-NSString *const MGLEventKeySpeed = @"speed";
-NSString *const MGLEventKeyCourse = @"course";
-NSString *const MGLEventKeyGestureID = @"gesture";
-NSString *const MGLEventHorizontalAccuracy = @"horizontalAccuracy";
-NSString *const MGLEventKeyLocalDebugDescription = @"debug.description";
-
-static NSString *const MGLEventKeyEvent = @"event";
-static NSString *const MGLEventKeyCreated = @"created";
-static NSString *const MGLEventKeyVendorID = @"userId";
-static NSString *const MGLEventKeyModel = @"model";
-static NSString *const MGLEventKeyEnabledTelemetry = @"enabled.telemetry";
-static NSString *const MGLEventKeyOperatingSystem = @"operatingSystem";
-static NSString *const MGLEventKeyResolution = @"resolution";
-static NSString *const MGLEventKeyAccessibilityFontScale = @"accessibilityFontScale";
-static NSString *const MGLEventKeyOrientation = @"orientation";
-static NSString *const MGLEventKeyPluggedIn = @"pluggedIn";
-static NSString *const MGLEventKeyWifi = @"wifi";
-static NSString *const MGLEventKeySource = @"source";
-static NSString *const MGLEventKeySessionId = @"sessionId";
-static NSString *const MGLEventKeyApplicationState = @"applicationState";
-static NSString *const MGLEventKeyAltitude = @"altitude";
-
-static NSString *const MGLMapboxAccountType = @"MGLMapboxAccountType";
-static NSString *const MGLMapboxMetricsEnabled = @"MGLMapboxMetricsEnabled";
-
-// SDK event source
-static NSString *const MGLEventSource = @"mapbox";
-
-// Event application state
-static NSString *const MGLApplicationStateForeground = @"Foreground";
-static NSString *const MGLApplicationStateBackground = @"Background";
-static NSString *const MGLApplicationStateInactive = @"Inactive";
-static NSString *const MGLApplicationStateUnknown = @"Unknown";
-
-const NSUInteger MGLMaximumEventsPerFlush = 180;
-const NSTimeInterval MGLFlushInterval = 180;
-
-@interface MGLMapboxEventsData : NSObject
-
-@property (nonatomic) NSString *vendorId;
-@property (nonatomic) NSString *model;
-@property (nonatomic) NSString *iOSVersion;
-@property (nonatomic) CGFloat scale;
-
-@end
-
-@implementation MGLMapboxEventsData
-
-- (instancetype)init {
- if (self = [super init]) {
- _vendorId = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
- _model = [self sysInfoByName:"hw.machine"];
- _iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion];
- if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
- _scale = [UIScreen mainScreen].nativeScale;
- } else {
- _scale = [UIScreen mainScreen].scale;
- }
- }
- return self;
-}
-
-- (NSString *)sysInfoByName:(char *)typeSpecifier {
- size_t size;
- sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
-
- char *answer = malloc(size);
- sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
-
- NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
-
- free(answer);
- return results;
-}
+@property (nonatomic) MMEEventsManager *eventsManager;
+@property (nonatomic) NSURL *baseURL;
+@property (nonatomic, copy) NSString *accessToken;
@end
+@implementation MGLMapboxEvents
-@interface MGLMapboxEvents () <MGLLocationManagerDelegate>
-
-@property (nonatomic) MGLMapboxEventsData *data;
-@property (nonatomic, copy) NSString *appBundleId;
-@property (nonatomic, readonly) NSString *instanceID;
-@property (nonatomic, copy) NSString *dateForDebugLogFile;
-@property (nonatomic) NSDateFormatter *rfc3339DateFormatter;
-@property (nonatomic) MGLAPIClient *apiClient;
-@property (nonatomic) BOOL usesTestServer;
-@property (nonatomic) BOOL canEnableDebugLogging;
-@property (nonatomic, getter=isPaused) BOOL paused;
-@property (nonatomic) NS_MUTABLE_ARRAY_OF(MGLMapboxEventAttributes *) *eventQueue;
-@property (nonatomic) dispatch_queue_t serialQueue;
-@property (nonatomic) dispatch_queue_t debugLogSerialQueue;
-@property (nonatomic) MGLLocationManager *locationManager;
-@property (nonatomic) NSTimer *timer;
-@property (nonatomic) NSDate *instanceIDRotationDate;
-@property (nonatomic) NSDate *nextTurnstileSendDate;
-@property (nonatomic) NSNumber *currentAccountTypeValue;
-@property (nonatomic) BOOL currentMetricsEnabledValue;
-
-@end
-
-@implementation MGLMapboxEvents {
- NSString *_instanceID;
- UIBackgroundTaskIdentifier _backgroundTaskIdentifier;
-}
+ (void)initialize {
if (self == [MGLMapboxEvents class]) {
NSBundle *bundle = [NSBundle mainBundle];
NSNumber *accountTypeNumber = [bundle objectForInfoDictionaryKey:MGLMapboxAccountType];
- [[NSUserDefaults standardUserDefaults] registerDefaults:@{
- MGLMapboxAccountType: accountTypeNumber ?: @0,
- MGLMapboxMetricsEnabled: @YES,
- @"MGLMapboxMetricsDebugLoggingEnabled": @NO,
- }];
+ [[NSUserDefaults standardUserDefaults] registerDefaults:@{MGLMapboxAccountType: accountTypeNumber ?: @0,
+ MGLMapboxMetricsEnabled: @YES,
+ MGLMapboxMetricsDebugLoggingEnabled: @NO}];
}
}
-+ (BOOL)isEnabled {
-#if TARGET_OS_SIMULATOR
- return NO;
-#else
- BOOL isLowPowerModeEnabled = NO;
- if ([NSProcessInfo instancesRespondToSelector:@selector(isLowPowerModeEnabled)]) {
- isLowPowerModeEnabled = [[NSProcessInfo processInfo] isLowPowerModeEnabled];
++ (nullable instancetype)sharedInstance {
+ if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
+ return nil;
}
- return ([[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled] &&
- [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0 &&
- !isLowPowerModeEnabled);
-#endif
-}
-
-
-- (BOOL)debugLoggingEnabled {
- return (self.canEnableDebugLogging &&
- [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]);
+
+ static dispatch_once_t onceToken;
+ static MGLMapboxEvents *_sharedInstance;
+ dispatch_once(&onceToken, ^{
+ _sharedInstance = [[self alloc] init];
+ });
+ return _sharedInstance;
}
-- (instancetype) init {
+- (instancetype)init {
self = [super init];
if (self) {
- [MGLTelemetryConfig.sharedConfig configurationFromKey:[[NSUserDefaults standardUserDefaults] objectForKey:MGLMapboxMetricsProfile]];
+ _eventsManager = [[MMEEventsManager alloc] init];
+ _eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsDebugLoggingEnabled];
+ _eventsManager.accountType = [[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType];
+ _eventsManager.metricsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:MGLMapboxMetricsEnabled];
- _currentAccountTypeValue = @0;
- _currentMetricsEnabledValue = YES;
-
- _appBundleId = [[NSBundle mainBundle] bundleIdentifier];
- _apiClient = [[MGLAPIClient alloc] init];
-
- NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
- _serialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.serial", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
-
- _locationManager = [[MGLLocationManager alloc] init];
- _locationManager.delegate = self;
- _paused = YES;
- [self resumeMetricsCollection];
-
- // Events Control
- _eventQueue = [[NSMutableArray alloc] init];
-
- // Setup Date Format
- _rfc3339DateFormatter = [[NSDateFormatter alloc] init];
- NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
-
- [_rfc3339DateFormatter setLocale:enUSPOSIXLocale];
- [_rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"];
- // Clear Any System TimeZone Cache
- [NSTimeZone resetSystemTimeZone];
- [_rfc3339DateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
-
- // Configure logging
- if ([self isProbablyAppStoreBuild]) {
- self.canEnableDebugLogging = NO;
-
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"]) {
- NSLog(@"Telemetry logging is only enabled in non-app store builds.");
- }
- } else {
- self.canEnableDebugLogging = YES;
+ // It is possible for the shared instance of this class to be created because of a call to
+ // +[MGLAccountManager load] early on in the app lifecycle of the host application.
+ // If user default values for access token and base URL are available, they are stored here
+ // on local properties so that they can be applied later once MMEEventsManager is fully initialized
+ // (once -[MMEEventsManager initializeWithAccessToken:userAgentBase:hostSDKVersion:] is called.
+ // Normally, the telem access token and base URL are not set this way. However, overriding these values
+ // with user defaults can be useful for testing with an alternative (test) backend system.
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) {
+ self.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
}
-
- // Watch for changes to telemetry settings by the user
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
-
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidEnterBackgroundNotification object:nil];
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:UIApplicationDidBecomeActiveNotification object:nil];
-
- // Watch for Low Power Mode change events
- if (&NSProcessInfoPowerStateDidChangeNotification != NULL) {
- [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseOrResumeMetricsCollectionIfRequired) name:NSProcessInfoPowerStateDidChangeNotification object:nil];
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
+ self.baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
}
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userDefaultsDidChange:) name:NSUserDefaultsDidChangeNotification object:nil];
}
return self;
}
-// Called implicitly from any public class convenience methods.
-// May return nil if this feature is disabled.
-//
-+ (nullable instancetype)sharedManager {
- if (NSProcessInfo.processInfo.mgl_isInterfaceBuilderDesignablesAgent) {
- return nil;
- }
- static dispatch_once_t onceToken;
- static MGLMapboxEvents *_sharedManager;
- dispatch_once(&onceToken, ^{
- _sharedManager = [[self alloc] init];
- });
- return _sharedManager;
-}
-
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
- [self pauseMetricsCollection];
}
-- (NSString *)instanceID {
- if (self.instanceIDRotationDate && [[NSDate date] timeIntervalSinceDate:self.instanceIDRotationDate] >= 0) {
- _instanceID = nil;
+- (void)userDefaultsDidChange:(NSNotification *)notification {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self updateNonDisablingConfigurationValues];
+ [self updateDisablingConfigurationValuesWithNotification:notification];
+ });
+}
+
+- (void)updateNonDisablingConfigurationValues {
+ self.eventsManager.debugLoggingEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"MGLMapboxMetricsDebugLoggingEnabled"];
+
+ // It is possible for `MGLTelemetryAccessToken` to have been set yet `userDefaultsDidChange:`
+ // is called before `setupWithAccessToken:` is called.
+ // In that case, setting the access token here will have no effect. In practice, that's fine
+ // because the access token value will be resolved when `setupWithAccessToken:` is called eventually
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryAccessToken]) {
+ self.eventsManager.accessToken = [[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryAccessToken];
}
- if (!_instanceID) {
- _instanceID = [[NSUUID UUID] UUIDString];
- NSTimeInterval twentyFourHourTimeInterval = 24 * 3600;
- self.instanceIDRotationDate = [[NSDate date] dateByAddingTimeInterval:twentyFourHourTimeInterval];
+
+ // It is possible for `MGLTelemetryBaseURL` to have been set yet `userDefaultsDidChange:`
+ // is called before setupWithAccessToken: is called.
+ // In that case, setting the base URL here will have no effect. In practice, that's fine
+ // because the base URL value will be resolved when `setupWithAccessToken:` is called eventually
+ if ([[[[NSUserDefaults standardUserDefaults] dictionaryRepresentation] allKeys] containsObject:MGLTelemetryBaseURL]) {
+ NSURL *baseURL = [NSURL URLWithString:[[NSUserDefaults standardUserDefaults] objectForKey:MGLTelemetryBaseURL]];
+ self.eventsManager.baseURL = baseURL;
}
- return _instanceID;
}
-- (void)userDefaultsDidChange:(NSNotification *)notification {
-
+- (void)updateDisablingConfigurationValuesWithNotification:(NSNotification *)notification {
// Guard against over calling pause / resume if the values this implementation actually
- // cares about have not changed
-
+ // cares about have not changed. We guard because the pause and resume method checks CoreLocation's
+ // authorization status and that can drag on the main thread if done too many times (e.g. if the host
+ // app heavily uses the user defaults API and this method is called very frequently)
if ([[notification object] respondsToSelector:@selector(objectForKey:)]) {
NSUserDefaults *userDefaults = [notification object];
- NSNumber *accountType = [userDefaults objectForKey:MGLMapboxAccountType];
- BOOL metricsEnabled = [[userDefaults objectForKey:MGLMapboxMetricsEnabled] boolValue];
-
- if (![accountType isEqualToNumber:self.currentAccountTypeValue] || metricsEnabled != self.currentMetricsEnabledValue) {
- [self pauseOrResumeMetricsCollectionIfRequired];
- self.currentAccountTypeValue = accountType;
- self.currentMetricsEnabledValue = metricsEnabled;
- }
- }
-
-}
-
-- (void)pauseOrResumeMetricsCollectionIfRequired {
-
- // [CLLocationManager authorizationStatus] has been found to block in some cases so
- // dispatch the call to a non-UI thread
- dispatch_async(self.serialQueue, ^{
- CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
+ NSInteger accountType = [userDefaults integerForKey:MGLMapboxAccountType];
+ BOOL metricsEnabled = [userDefaults boolForKey:MGLMapboxMetricsEnabled];
- // Checking application state must be done on the main thread for safety and
- // to avoid a thread sanitizer error
- dispatch_async(dispatch_get_main_queue(), ^{
- UIApplication *application = [UIApplication sharedApplication];
- UIApplicationState state = application.applicationState;
-
- // Prevent blue status bar when host app has `when in use` permission only and it is not in foreground
- if (status == kCLAuthorizationStatusAuthorizedWhenInUse && state == UIApplicationStateBackground) {
- if (_backgroundTaskIdentifier == UIBackgroundTaskInvalid) {
- _backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^{
- [application endBackgroundTask:_backgroundTaskIdentifier];
- _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
- }];
- [self flush];
- }
- [self pauseMetricsCollection];
- return;
- }
+ if (accountType != self.eventsManager.accountType || metricsEnabled != self.eventsManager.metricsEnabled) {
+ self.eventsManager.accountType = accountType;
+ self.eventsManager.metricsEnabled = metricsEnabled;
- // Toggle pause based on current pause state, user opt-out state, and low-power state.
- BOOL enabled = [[self class] isEnabled];
- if (self.paused && enabled) {
- [self resumeMetricsCollection];
- } else if (!self.paused && !enabled) {
- [self flush];
- [self pauseMetricsCollection];
- }
- });
- });
-}
-
-- (void)pauseMetricsCollection {
- if (self.paused) {
- return;
- }
-
- self.paused = YES;
- [self.timer invalidate];
- self.timer = nil;
- [self.eventQueue removeAllObjects];
- self.data = nil;
-
- [self.locationManager stopUpdatingLocation];
-}
-
-- (void)resumeMetricsCollection {
- if (!self.paused || ![[self class] isEnabled]) {
- return;
- }
-
- self.paused = NO;
- self.data = [[MGLMapboxEventsData alloc] init];
-
- [self.locationManager startUpdatingLocation];
-}
-
-+ (void)flush {
- [[MGLMapboxEvents sharedManager] flush];
-}
-
-- (void)flush {
- if ([MGLAccountManager accessToken] == nil) {
- return;
- }
-
- NSArray *events = [NSArray arrayWithArray:self.eventQueue];
- [self.eventQueue removeAllObjects];
-
- [self postEvents:events];
-
- if (self.timer) {
- [self.timer invalidate];
- self.timer = nil;
- }
-
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"flush"}];
-}
-
-- (void)pushTurnstileEvent {
- if (self.nextTurnstileSendDate && [[NSDate date] timeIntervalSinceDate:self.nextTurnstileSendDate] < 0) {
- return;
- }
-
- NSString *vendorID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
- if (!vendorID) {
- return;
- }
-
- NSDictionary *turnstileEventAttributes = @{MGLEventKeyEvent: MGLEventTypeAppUserTurnstile,
- MGLEventKeyCreated: [self.rfc3339DateFormatter stringFromDate:[NSDate date]],
- MGLEventKeyVendorID: vendorID,
- MGLEventKeyEnabledTelemetry: @([[self class] isEnabled])};
-
- if ([MGLAccountManager accessToken] == nil) {
- return;
- }
-
- __weak __typeof__(self) weakSelf = self;
- [self.apiClient postEvent:turnstileEventAttributes completionHandler:^(NSError * _Nullable error) {
- __strong __typeof__(weakSelf) strongSelf = weakSelf;
- if (error) {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error",
- @"error": error}];
- return;
+ [self.eventsManager pauseOrResumeMetricsCollectionIfRequired];
}
- [strongSelf writeEventToLocalDebugLog:turnstileEventAttributes];
- [strongSelf updateNextTurnstileSendDate];
- }];
-}
-
-- (void)updateNextTurnstileSendDate {
- // Find the time a day from now (sometime tomorrow)
- NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
- NSDateComponents *dayComponent = [[NSDateComponents alloc] init];
- dayComponent.day = 1;
- NSDate *sometimeTomorrow = [calendar dateByAddingComponents:dayComponent toDate:[NSDate date] options:0];
-
- // Find the start of tomorrow and use that as the next turnstile send date. The effect of this is that
- // turnstile events can be sent as much as once per calendar day and always at the start of a session
- // when a map load happens.
- NSDate *startOfTomorrow = nil;
- [calendar rangeOfUnit:NSCalendarUnitDay startDate:&startOfTomorrow interval:nil forDate:sometimeTomorrow];
- self.nextTurnstileSendDate = startOfTomorrow;
-}
-
-+ (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- [[MGLMapboxEvents sharedManager] pushEvent:event withAttributes:attributeDictionary];
-}
-
-- (void)pushEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if (!event) {
- return;
- }
-
- if ([event isEqualToString:MGLEventTypeMapLoad]) {
- [self pushTurnstileEvent];
- }
-
- if (self.paused) {
- return;
- }
-
- MGLMapboxEventAttributes *fullyFormedEvent = [self fullyFormedEventForEvent:event withAttributes:attributeDictionary];
- if (fullyFormedEvent) {
- [self.eventQueue addObject:fullyFormedEvent];
- [self writeEventToLocalDebugLog:fullyFormedEvent];
- // Has Flush Limit Been Reached?
- if (self.eventQueue.count >= MGLMaximumEventsPerFlush) {
- [self flush];
- } else if (self.eventQueue.count == 1) {
- // If this is first new event on queue start timer,
- [self startTimer];
- }
- } else {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Unknown event",
- @"eventName": event,
- @"event.attributes": attributeDictionary}];
- }
-}
-
-#pragma mark Events
-
-- (MGLMapboxEventAttributes *)fullyFormedEventForEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if ([event isEqualToString:MGLEventTypeMapLoad]) {
- return [self mapLoadEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeMapTap]) {
- return [self mapClickEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeMapDragEnd]) {
- return [self mapDragEndEventWithAttributes:attributeDictionary];
- } else if ([event isEqualToString:MGLEventTypeLocation]) {
- return [self locationEventWithAttributes:attributeDictionary];
}
- return nil;
-}
-
-- (MGLMapboxEventAttributes *)locationEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyEvent] = MGLEventTypeLocation;
- attributes[MGLEventKeySource] = MGLEventSource;
- attributes[MGLEventKeySessionId] = self.instanceID;
- attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
- NSString *currentApplicationState = [self applicationState];
- if (![currentApplicationState isEqualToString:MGLApplicationStateUnknown]) {
- attributes[MGLEventKeyApplicationState] = currentApplicationState;
- }
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapLoadEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapLoad;
- attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
- attributes[MGLEventKeyVendorID] = self.data.vendorId;
- attributes[MGLEventKeyModel] = self.data.model;
- attributes[MGLEventKeyOperatingSystem] = self.data.iOSVersion;
- attributes[MGLEventKeyResolution] = @(self.data.scale);
- attributes[MGLEventKeyAccessibilityFontScale] = @([self contentSizeScale]);
- attributes[MGLEventKeyOrientation] = [self deviceOrientation];
- attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapClickEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [self interactionEvent];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapTap;
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
-}
-
-- (MGLMapboxEventAttributes *)mapDragEndEventWithAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- MGLMutableMapboxEventAttributes *attributes = [self interactionEvent];
- attributes[MGLEventKeyEvent] = MGLEventTypeMapDragEnd;
-
- return [self eventForAttributes:attributes attributeDictionary:attributeDictionary];
}
-- (MGLMutableMapboxEventAttributes *)interactionEvent {
- MGLMutableMapboxEventAttributes *attributes = [NSMutableDictionary dictionary];
- attributes[MGLEventKeyCreated] = [self.rfc3339DateFormatter stringFromDate:[NSDate date]];
- attributes[MGLEventKeyOrientation] = [self deviceOrientation];
- attributes[MGLEventKeyWifi] = @([[MGLReachability reachabilityForLocalWiFi] isReachableViaWiFi]);
-
- return attributes;
-}
-
-- (MGLMapboxEventAttributes *)eventForAttributes:(MGLMutableMapboxEventAttributes *)attributes attributeDictionary:(MGLMapboxEventAttributes *)attributeDictionary {
- [attributes addEntriesFromDictionary:attributeDictionary];
-
- return [attributes copy];
-}
-
-// Called implicitly from public use of +flush.
-//
-- (void)postEvents:(NS_ARRAY_OF(MGLMapboxEventAttributes *) *)events {
- if (self.paused) {
- return;
++ (void)setupWithAccessToken:(NSString *)accessToken {
+ NSString *semanticVersion = [NSBundle mgl_frameworkInfoDictionary][@"MGLSemanticVersionString"];
+ NSString *shortVersion = [NSBundle mgl_frameworkInfoDictionary][@"CFBundleShortVersionString"];
+ NSString *sdkVersion = semanticVersion ?: shortVersion;
+
+ // It is possible that an alternative access token was already set on this instance when the class was loaded
+ // Use it if it exists
+ NSString *resolvedAccessToken = [MGLMapboxEvents sharedInstance].accessToken ?: accessToken;
+
+ [[[self sharedInstance] eventsManager] initializeWithAccessToken:resolvedAccessToken userAgentBase:MGLAPIClientUserAgentBase hostSDKVersion:sdkVersion];
+
+ // It is possible that an alternative base URL was set on this instance when the class was loaded
+ // Use it if it exists
+ if ([MGLMapboxEvents sharedInstance].baseURL) {
+ [[MGLMapboxEvents sharedInstance] eventsManager].baseURL = [MGLMapboxEvents sharedInstance].baseURL;
}
-
- __weak __typeof__(self) weakSelf = self;
- dispatch_async(self.serialQueue, ^{
- __strong __typeof__(weakSelf) strongSelf = weakSelf;
- [self.apiClient postEvents:events completionHandler:^(NSError * _Nullable error) {
- if (error) {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"Network error",
- @"error": error}];
- } else {
- [strongSelf pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"post",
- @"debug.eventsCount": @(events.count)}];
- }
- [[UIApplication sharedApplication] endBackgroundTask:_backgroundTaskIdentifier];
- _backgroundTaskIdentifier = UIBackgroundTaskInvalid;
- }];
- });
-}
-
-- (void)startTimer {
- [self.timer invalidate];
- self.timer = [NSTimer scheduledTimerWithTimeInterval:MGLFlushInterval
- target:self
- selector:@selector(flush)
- userInfo:nil
- repeats:YES];
}
-- (NSString *)deviceOrientation {
- NSString *result;
-
- switch ([UIDevice currentDevice].orientation) {
- case UIDeviceOrientationUnknown:
- result = @"Unknown";
- break;
- case UIDeviceOrientationPortrait:
- result = @"Portrait";
- break;
- case UIDeviceOrientationPortraitUpsideDown:
- result = @"PortraitUpsideDown";
- break;
- case UIDeviceOrientationLandscapeLeft:
- result = @"LandscapeLeft";
- break;
- case UIDeviceOrientationLandscapeRight:
- result = @"LandscapeRight";
- break;
- case UIDeviceOrientationFaceUp:
- result = @"FaceUp";
- break;
- case UIDeviceOrientationFaceDown:
- result = @"FaceDown";
- break;
- default:
- result = @"Default - Unknown";
- break;
- }
-
- return result;
++ (void)pushTurnstileEvent {
+ [[[self sharedInstance] eventsManager] sendTurnstileEvent];
}
-- (NSString *)applicationState {
- switch ([UIApplication sharedApplication].applicationState) {
- case UIApplicationStateActive:
- return MGLApplicationStateForeground;
- case UIApplicationStateInactive:
- return MGLApplicationStateInactive;
- case UIApplicationStateBackground:
- return MGLApplicationStateBackground;
- default:
- return MGLApplicationStateUnknown;
- }
++ (void)pushEvent:(NSString *)event withAttributes:(MMEMapboxEventAttributes *)attributeDictionary {
+ [[[self sharedInstance] eventsManager] enqueueEventWithName:event attributes:attributeDictionary];
}
-- (NSInteger)contentSizeScale {
- NSInteger result = -9999;
-
- NSString *sc = [UIApplication sharedApplication].preferredContentSizeCategory;
-
- if ([sc isEqualToString:UIContentSizeCategoryExtraSmall]) {
- result = -3;
- } else if ([sc isEqualToString:UIContentSizeCategorySmall]) {
- result = -2;
- } else if ([sc isEqualToString:UIContentSizeCategoryMedium]) {
- result = -1;
- } else if ([sc isEqualToString:UIContentSizeCategoryLarge]) {
- result = 0;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraLarge]) {
- result = 1;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
- result = 2;
- } else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
- result = 3;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
- result = -11;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
- result = 10;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
- result = 11;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
- result = 12;
- } else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
- result = 13;
- }
-
- return result;
++ (void)flush {
+ [[[self sharedInstance] eventsManager] flush];
}
+ (void)ensureMetricsOptoutExists {
NSNumber *shownInAppNumber = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"MGLMapboxMetricsEnabledSettingShownInApp"];
BOOL metricsEnabledSettingShownInAppFlag = [shownInAppNumber boolValue];
-
+
if (!metricsEnabledSettingShownInAppFlag &&
[[NSUserDefaults standardUserDefaults] integerForKey:MGLMapboxAccountType] == 0) {
// Opt-out is not configured in UI, so check for Settings.bundle
id defaultEnabledValue;
NSString *appSettingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
-
+
if (appSettingsBundle) {
// Dynamic Settings.bundle loading based on http://stackoverflow.com/a/510329/2094275
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[appSettingsBundle stringByAppendingPathComponent:@"Root.plist"]];
@@ -651,7 +171,7 @@ const NSTimeInterval MGLFlushInterval = 180;
}
}
}
-
+
if (!defaultEnabledValue) {
[NSException raise:@"Telemetry opt-out missing" format:
@"End users must be able to opt out of Mapbox Telemetry in your app, either inside Settings (via Settings.bundle) or inside this app. "
@@ -663,153 +183,4 @@ const NSTimeInterval MGLFlushInterval = 180;
}
}
-#pragma mark CLLocationManagerUtilityDelegate
-
-- (void)locationManager:(MGLLocationManager *)locationManager didUpdateLocations:(NSArray *)locations {
- for (CLLocation *loc in locations) {
- double accuracy = 10000000;
- double lat = floor(loc.coordinate.latitude * accuracy) / accuracy;
- double lng = floor(loc.coordinate.longitude * accuracy) / accuracy;
- double horizontalAccuracy = round(loc.horizontalAccuracy);
- NSString *formattedDate = [self.rfc3339DateFormatter stringFromDate:loc.timestamp];
- [MGLMapboxEvents pushEvent:MGLEventTypeLocation withAttributes:@{MGLEventKeyCreated: formattedDate,
- MGLEventKeyLatitude: @(lat),
- MGLEventKeyLongitude: @(lng),
- MGLEventKeyAltitude: @(round(loc.altitude)),
- MGLEventHorizontalAccuracy: @(horizontalAccuracy)}];
- }
-}
-
-- (void)locationManagerBackgroundLocationUpdatesDidAutomaticallyPause:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerAutoPause"}];
-}
-
-- (void)locationManagerBackgroundLocationUpdatesDidTimeout:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerTimeout"}];
-}
-
-- (void)locationManagerDidStartLocationUpdates:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription:@"locationManager.locationManagerStartUpdates"}];
-}
-
-- (void)locationManagerDidStopLocationUpdates:(MGLLocationManager *)locationManager {
- [self pushDebugEvent:MGLEventTypeLocalDebug withAttributes:@{MGLEventKeyLocalDebugDescription: @"locationManager.locationManagerStopUpdates"}];
-}
-
-#pragma mark MGLMapboxEvents Debug
-
-- (void)pushDebugEvent:(NSString *)event withAttributes:(MGLMapboxEventAttributes *)attributeDictionary {
- if (![self debugLoggingEnabled]) {
- return;
- }
-
- if (!event) {
- return;
- }
-
- MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:attributeDictionary];
- [evt setObject:event forKey:@"event"];
- [evt setObject:[self.rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"created"];
- [evt setValue:[self applicationState] forKey:@"applicationState"];
- [evt setValue:@([[self class] isEnabled]) forKey:@"telemetryEnabled"];
- [evt setObject:self.instanceID forKey:@"instance"];
-
- MGLMapboxEventAttributes *finalEvent = [NSDictionary dictionaryWithDictionary:evt];
- [self writeEventToLocalDebugLog:finalEvent];
-}
-
-- (void)writeEventToLocalDebugLog:(MGLMapboxEventAttributes *)event {
- if (![self debugLoggingEnabled]) {
- return;
- }
-
- NSLog(@"%@", [self stringForDebugEvent:event]);
-
- if (!self.dateForDebugLogFile) {
- NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
- [dateFormatter setDateFormat:@"yyyy'-'MM'-'dd"];
- [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
- self.dateForDebugLogFile = [dateFormatter stringFromDate:[NSDate date]];
- }
-
- if (!self.debugLogSerialQueue) {
- NSString *uniqueID = [[NSProcessInfo processInfo] globallyUniqueString];
- self.debugLogSerialQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@.%@.events.debugLog", _appBundleId, uniqueID] UTF8String], DISPATCH_QUEUE_SERIAL);
- }
-
- dispatch_async(self.debugLogSerialQueue, ^{
- if ([NSJSONSerialization isValidJSONObject:event]) {
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:event options:NSJSONWritingPrettyPrinted error:nil];
-
- NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
- jsonString = [jsonString stringByAppendingString:@",\n"];
-
- NSString *logFilePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject] stringByAppendingPathComponent:[NSString stringWithFormat:@"telemetry_log-%@.json", self.dateForDebugLogFile]];
-
- NSFileManager *fileManager = [[NSFileManager alloc] init];
- if ([fileManager fileExistsAtPath:logFilePath]) {
- NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
- [fileHandle seekToEndOfFile];
- [fileHandle writeData:[jsonString dataUsingEncoding:NSUTF8StringEncoding]];
- } else {
- [fileManager createFileAtPath:logFilePath contents:[jsonString dataUsingEncoding:NSUTF8StringEncoding] attributes:@{ NSFileProtectionKey: NSFileProtectionCompleteUntilFirstUserAuthentication }];
- }
- }
- });
-}
-
-- (NSString *)stringForDebugEvent:(MGLMapboxEventAttributes *)event {
- // redact potentially sensitive location details from system console log
- if ([event[@"event"] isEqualToString:MGLEventTypeLocation]) {
- MGLMutableMapboxEventAttributes *evt = [MGLMutableMapboxEventAttributes dictionaryWithDictionary:event];
- [evt setObject:@"<redacted>" forKey:@"lat"];
- [evt setObject:@"<redacted>" forKey:@"lng"];
- event = evt;
- }
-
- return [NSString stringWithFormat:@"Mapbox Telemetry event %@", event];
-}
-
-- (BOOL)isProbablyAppStoreBuild {
-#if TARGET_IPHONE_SIMULATOR
- return NO;
-#else
- // BugshotKit by Marco Arment https://github.com/marcoarment/BugshotKit/
- // Adapted from https://github.com/blindsightcorp/BSMobileProvision
-
- NSString *binaryMobileProvision = [NSString stringWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"] encoding:NSISOLatin1StringEncoding error:NULL];
- if (!binaryMobileProvision) {
- return YES; // no provision
- }
-
- NSScanner *scanner = [NSScanner scannerWithString:binaryMobileProvision];
- NSString *plistString;
- if (![scanner scanUpToString:@"<plist" intoString:nil] || ! [scanner scanUpToString:@"</plist>" intoString:&plistString]) {
- return YES; // no XML plist found in provision
- }
- plistString = [plistString stringByAppendingString:@"</plist>"];
-
- NSData *plistdata_latin1 = [plistString dataUsingEncoding:NSISOLatin1StringEncoding];
- NSError *error = nil;
- NSDictionary *mobileProvision = [NSPropertyListSerialization propertyListWithData:plistdata_latin1 options:NSPropertyListImmutable format:NULL error:&error];
- if (error) {
- return YES; // unknown plist format
- }
-
- if (!mobileProvision || ! mobileProvision.count) {
- return YES; // no entitlements
- }
-
- if (mobileProvision[@"ProvisionsAllDevices"]) {
- return NO; // enterprise provisioning
- }
-
- if (mobileProvision[@"ProvisionedDevices"] && [mobileProvision[@"ProvisionedDevices"] count]) {
- return NO; // development or ad-hoc
- }
-
- return YES; // expected development/enterprise/ad-hoc entitlements not found
-#endif
-}
-
@end
diff --git a/platform/ios/src/MGLScaleBar.mm b/platform/ios/src/MGLScaleBar.mm
index 139dffdfab..a2fc24c75c 100644
--- a/platform/ios/src/MGLScaleBar.mm
+++ b/platform/ios/src/MGLScaleBar.mm
@@ -178,12 +178,7 @@ static const CGFloat MGLFeetPerMeter = 3.28084;
#pragma mark - Convenience methods
- (BOOL)usesRightToLeftLayout {
- // semanticContentAttribute is iOS 9+
- if ([self.superview respondsToSelector:@selector(semanticContentAttribute)]) {
- return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
- } else {
- return UIApplication.sharedApplication.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
- }
+ return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.superview.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
}
- (BOOL)usesMetricSystem {
diff --git a/platform/ios/src/MGLTelemetryConfig.h b/platform/ios/src/MGLTelemetryConfig.h
index 527d344291..96e525c969 100644
--- a/platform/ios/src/MGLTelemetryConfig.h
+++ b/platform/ios/src/MGLTelemetryConfig.h
@@ -9,7 +9,7 @@ NS_ASSUME_NONNULL_BEGIN
extern NSString *const MGLMapboxMetricsProfile;
-+ (nullable instancetype)sharedConfig;
+@property (class, nullable, nonatomic, readonly) MGLTelemetryConfig *sharedConfig;
- (void)configurationFromKey:(NSString *)key;
diff --git a/platform/ios/src/MGLUserLocation.m b/platform/ios/src/MGLUserLocation.m
index 074d138a72..245cbf4371 100644
--- a/platform/ios/src/MGLUserLocation.m
+++ b/platform/ios/src/MGLUserLocation.m
@@ -68,7 +68,7 @@ NS_ASSUME_NONNULL_END
return ! [key isEqualToString:@"location"] && ! [key isEqualToString:@"heading"];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCoordinate
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCoordinate
{
return [NSSet setWithObject:@"location"];
}
diff --git a/platform/ios/src/Mapbox-Prefix.pch b/platform/ios/src/Mapbox-Prefix.pch
new file mode 100644
index 0000000000..6754020861
--- /dev/null
+++ b/platform/ios/src/Mapbox-Prefix.pch
@@ -0,0 +1 @@
+#import "MMENamespacedDependencies.h"
diff --git a/platform/ios/src/Mapbox.h b/platform/ios/src/Mapbox.h
index 9b2c472cf6..20417dbbd4 100644
--- a/platform/ios/src/Mapbox.h
+++ b/platform/ios/src/Mapbox.h
@@ -45,13 +45,17 @@ 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 "MGLVectorTileSource.h"
#import "MGLShapeSource.h"
-#import "MGLRasterSource.h"
+#import "MGLComputedShapeSource.h"
+#import "MGLRasterTileSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
@@ -61,3 +65,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/UIColor+MGLAdditions.h b/platform/ios/src/UIColor+MGLAdditions.h
index ea415d9db9..60cfe1c58b 100644
--- a/platform/ios/src/UIColor+MGLAdditions.h
+++ b/platform/ios/src/UIColor+MGLAdditions.h
@@ -12,3 +12,10 @@
+ (UIColor *)mgl_colorWithColor:(mbgl::Color)color;
@end
+
+@interface NSExpression (MGLColorAdditions)
+
++ (NSExpression *)mgl_expressionForRGBComponents:(NSArray<NSExpression *> *)components;
++ (NSExpression *)mgl_expressionForRGBAComponents:(NSArray<NSExpression *> *)components;
+
+@end
diff --git a/platform/ios/src/UIColor+MGLAdditions.mm b/platform/ios/src/UIColor+MGLAdditions.mm
index 41c066c206..9ca39acda4 100644
--- a/platform/ios/src/UIColor+MGLAdditions.mm
+++ b/platform/ios/src/UIColor+MGLAdditions.mm
@@ -21,3 +21,52 @@
}
@end
+
+@implementation NSExpression (MGLColorAdditions)
+
++ (NSExpression *)mgl_expressionForRGBComponents:(NSArray<NSExpression *> *)components {
+ if (UIColor *color = [self mgl_colorWithRGBComponents:components]) {
+ return [NSExpression expressionForConstantValue:color];
+ }
+
+ NSExpression *color = [NSExpression expressionForConstantValue:[UIColor class]];
+ NSExpression *alpha = [NSExpression expressionForConstantValue:@1.0];
+ return [NSExpression expressionForFunction:color
+ selectorName:@"colorWithRed:green:blue:alpha:"
+ arguments:[components arrayByAddingObject:alpha]];
+}
+
++ (NSExpression *)mgl_expressionForRGBAComponents:(NSArray<NSExpression *> *)components {
+ if (UIColor *color = [self mgl_colorWithRGBComponents:components]) {
+ return [NSExpression expressionForConstantValue:color];
+ }
+
+ NSExpression *color = [NSExpression expressionForConstantValue:[UIColor class]];
+ return [NSExpression expressionForFunction:color
+ selectorName:@"colorWithRed:green:blue:alpha:"
+ arguments:components];
+}
+
++ (UIColor *)mgl_colorWithRGBComponents:(NSArray<NSExpression *> *)components {
+ if (components.count < 3 || components.count > 4) {
+ return nil;
+ }
+
+ for (NSExpression *component in components) {
+ if (component.expressionType != NSConstantValueExpressionType) {
+ return nil;
+ }
+
+ NSNumber *number = (NSNumber *)component.constantValue;
+ if (![number isKindOfClass:[NSNumber class]]) {
+ return nil;
+ }
+ }
+
+ return [UIColor colorWithRed:[components[0].constantValue doubleValue] / 255.0
+ green:[components[1].constantValue doubleValue] / 255.0
+ blue:[components[2].constantValue doubleValue] / 255.0
+ alpha:components.count == 3 ? [components[3].constantValue doubleValue] : 1.0];
+}
+
+@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/MGLAnnotationViewTests.m b/platform/ios/test/MGLAnnotationViewTests.m
index fc4f35a9e1..2f5963e66e 100644
--- a/platform/ios/test/MGLAnnotationViewTests.m
+++ b/platform/ios/test/MGLAnnotationViewTests.m
@@ -49,7 +49,7 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
_didCallDismissCalloutAnimated = YES;
}
-- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated { }
+- (void)presentCalloutFromRect:(CGRect)rect inView:(nonnull UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {}
@end
@@ -57,6 +57,7 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
@property (nonatomic) XCTestExpectation *expectation;
@property (nonatomic) MGLMapView *mapView;
@property (nonatomic, weak) MGLAnnotationView *annotationView;
+@property (nonatomic) NSInteger annotationSelectedCount;
@end
@implementation MGLAnnotationViewTests
@@ -98,6 +99,61 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
XCTAssertNotNil(customAnnotationView);
}
+- (void)testSelectingOffscreenAnnotation
+{
+ // Partial test for https://github.com/mapbox/mapbox-gl-native/issues/9790
+
+ // This isn't quite the same as in updateAnnotationViews, but should be sufficient for this test.
+ MGLCoordinateBounds coordinateBounds = [_mapView convertRect:_mapView.bounds toCoordinateBoundsFromView:_mapView];
+
+ // -90 latitude is invalid. TBD.
+ BOOL anyOffscreen = NO;
+ NSInteger selectionCount = 0;
+
+ for (NSInteger latitude = -89; latitude <= 90; latitude += 10)
+ {
+ for (NSInteger longitude = -180; longitude <= 180; longitude += 10)
+ {
+ MGLTestAnnotation *annotation = [[MGLTestAnnotation alloc] init];
+
+ annotation.coordinate = CLLocationCoordinate2DMake(latitude, longitude);
+ [_mapView addAnnotation:annotation];
+
+ if (!(MGLCoordinateInCoordinateBounds(annotation.coordinate, coordinateBounds)))
+ anyOffscreen = YES;
+
+ XCTAssertNil(_mapView.selectedAnnotations.firstObject, @"There should be no selected annotation");
+
+ // First selection
+ [_mapView selectAnnotation:annotation animated:NO];
+ selectionCount++;
+
+ XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should only be 1 selected annotation");
+ XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [_mapView deselectAnnotation:annotation animated:NO];
+ XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations");
+
+ // Second selection
+ _mapView.selectedAnnotations = @[annotation];
+ selectionCount++;
+
+ XCTAssert(_mapView.selectedAnnotations.count == 1, @"There should be 1 selected annotation");
+ XCTAssertEqualObjects(_mapView.selectedAnnotations.firstObject, annotation, @"The annotation should be selected");
+
+ // Deselect
+ [_mapView deselectAnnotation:annotation animated:NO];
+ XCTAssert(_mapView.selectedAnnotations.count == 0, @"There should be no selected annotations");
+ }
+ }
+
+ XCTAssert(anyOffscreen, @"At least one of these annotations should be offscreen");
+ XCTAssertEqual(selectionCount, self.annotationSelectedCount, @"-mapView:didSelectAnnotation: should be called for each selection");
+}
+
+#pragma mark - MGLMapViewDelegate -
+
- (MGLAnnotationView *)mapView:(MGLMapView *)mapView viewForAnnotation:(id<MGLAnnotation>)annotation
{
MGLAnnotationView *annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:MGLTestAnnotationReuseIdentifer];
@@ -117,4 +173,9 @@ static NSString * const MGLTestAnnotationReuseIdentifer = @"MGLTestAnnotationReu
[_expectation fulfill];
}
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id<MGLAnnotation>)annotation
+{
+ self.annotationSelectedCount++;
+}
+
@end
diff --git a/platform/ios/test/MGLMapAccessibilityElementTests.m b/platform/ios/test/MGLMapAccessibilityElementTests.m
index 5c79d85de1..916461e708 100644
--- a/platform/ios/test/MGLMapAccessibilityElementTests.m
+++ b/platform/ios/test/MGLMapAccessibilityElementTests.m
@@ -19,7 +19,7 @@
};
MGLFeatureAccessibilityElement *element = [[MGLFeatureAccessibilityElement alloc] initWithAccessibilityContainer:self feature:feature];
XCTAssertEqualObjects(element.accessibilityLabel, @"English", @"Accessibility label should be localized.");
-
+
feature.attributes = @{
@"name": @"Цинциннати",
@"name_en": @"Цинциннати",
diff --git a/platform/ios/test/MGLMapViewLayoutTests.m b/platform/ios/test/MGLMapViewLayoutTests.m
index a41e7695f9..16f6cdada3 100644
--- a/platform/ios/test/MGLMapViewLayoutTests.m
+++ b/platform/ios/test/MGLMapViewLayoutTests.m
@@ -37,14 +37,15 @@
self.styleLoadingExpectation = [self expectationWithDescription:@"Map view should finish loading style."];
[self waitForExpectationsWithTimeout:1 handler:nil];
+ self.mapView.showsScale = YES;
+
//set zoom and heading so that scale bar and compass will be shown
- [self.mapView setZoomLevel:4.5 animated:NO];
+ [self.mapView setZoomLevel:10.0 animated:NO];
[self.mapView.camera setHeading:12.0];
//invoke layout
[self.superView setNeedsLayout];
[self.superView layoutIfNeeded];
-
}
- (void)mapView:(MGLMapView *)mapView didFinishLoadingStyle:(MGLStyle *)style {
@@ -67,7 +68,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/MGLMapViewScaleBarTests.m b/platform/ios/test/MGLMapViewScaleBarTests.m
new file mode 100644
index 0000000000..11d1187263
--- /dev/null
+++ b/platform/ios/test/MGLMapViewScaleBarTests.m
@@ -0,0 +1,62 @@
+#import <Mapbox/Mapbox.h>
+#import <XCTest/XCTest.h>
+
+@interface MGLMapViewScaleBarTests : XCTestCase
+
+@property (nonatomic) MGLMapView *mapView;
+
+@end
+
+@implementation MGLMapViewScaleBarTests
+
+- (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];
+}
+
+- (void)tearDown {
+ self.mapView = nil;
+
+ [super tearDown];
+}
+
+- (void)testShowsScale {
+ UIView *scaleBar = self.mapView.scaleBar;
+
+ // Scale bar should not be enabled by default.
+ XCTAssertFalse(self.mapView.showsScale);
+ XCTAssertTrue(scaleBar.hidden);
+
+ self.mapView.showsScale = YES;
+
+ XCTAssertFalse(scaleBar.hidden);
+
+ // Scale bar should not be visible at default zoom (~z0), but it should be ready.
+ XCTAssertFalse(CGRectIsEmpty(scaleBar.frame));
+ XCTAssertEqual(scaleBar.alpha, 0);
+
+ self.mapView.zoomLevel = 15;
+ XCTAssertGreaterThan(scaleBar.alpha, 0);
+}
+
+- (void)testDirectlySettingScaleBarViewHiddenProperty {
+ UIView *scaleBar = self.mapView.scaleBar;
+
+ scaleBar.hidden = NO;
+ XCTAssertFalse(scaleBar.hidden);
+
+ // Directly setting `.hidden` after the map has finished initializing will not update the scale bar.
+ XCTAssertTrue(CGRectIsEmpty(scaleBar.frame));
+
+ // ... but triggering any camera event will update it.
+ self.mapView.zoomLevel = 1;
+ XCTAssertFalse(CGRectIsEmpty(scaleBar.frame));
+ XCTAssertEqual(scaleBar.alpha, 0);
+
+ self.mapView.zoomLevel = 15;
+ XCTAssertGreaterThan(scaleBar.alpha, 0);
+}
+@end
diff --git a/platform/ios/test/MGLNSOrthographyAdditionsTests.m b/platform/ios/test/MGLNSOrthographyAdditionsTests.m
new file mode 100644
index 0000000000..f30553e8f6
--- /dev/null
+++ b/platform/ios/test/MGLNSOrthographyAdditionsTests.m
@@ -0,0 +1,19 @@
+#import <XCTest/XCTest.h>
+
+#import "NSOrthography+MGLAdditions.h"
+#import "MGLVectorTileSource_Private.h"
+
+@interface MGLNSOrthographyAdditionsTests : XCTestCase
+
+@end
+
+@implementation MGLNSOrthographyAdditionsTests
+
+- (void)testStreetsLanguages {
+ for (NSString *language in [MGLVectorTileSource 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/ios/vendor/SMCalloutView/SMCalloutView.h b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
index 0b14913626..5bb73d4c84 100755
--- a/platform/ios/vendor/SMCalloutView/SMCalloutView.h
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.h
@@ -114,6 +114,18 @@ extern NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView;
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated;
/**
+ @brief Presents a callout view by adding it to "inView" and pointing at the given rect of inView's bounds.
+
+ @discussion Constrains the callout to the rect (in the space of the given view).
+
+ @param rect @c CGRect to present the view from
+ @param view view to 'constrain' the @c constrainedView to
+ @param constrainedRect Rect to constrain the callout to
+ @param animated @c BOOL if presentation should be animated
+ */
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated;
+
+/**
@brief Present a callout layer in the `layer` and pointing at the given rect of the `layer` bounds
@discussion Same as the view-based presentation, but inserts the callout into a CALayer hierarchy instead.
diff --git a/platform/ios/vendor/SMCalloutView/SMCalloutView.m b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
index 9631ca0367..a0049a3e2d 100755
--- a/platform/ios/vendor/SMCalloutView/SMCalloutView.m
+++ b/platform/ios/vendor/SMCalloutView/SMCalloutView.m
@@ -247,6 +247,35 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
return CGSizeMake(nudgeLeft ? nudgeLeft : nudgeRight, nudgeTop ? nudgeTop : nudgeBottom);
}
+- (UIEdgeInsets)marginInsetsHintForPresentationFromRect:(CGRect)rect {
+
+ // form our subviews based on our content set so far
+ [self rebuildSubviews];
+
+ // size the callout to fit the width constraint as best as possible
+ CGFloat height = self.calloutHeight;
+ CGSize size = [self sizeThatFits:CGSizeMake(0.0f, height)];
+
+ // Without re-jigging presentCalloutFromRect, let's just make a best-guess with what we have
+ // right now.
+ CGFloat horizontalMargin = fmaxf(0, ceilf((CALLOUT_MIN_WIDTH-rect.size.width)/2));
+
+ UIEdgeInsets insets = {
+ .top = 0.0f,
+ .right = -horizontalMargin,
+ .bottom = 0.0f,
+ .left = -horizontalMargin
+ };
+
+ if (self.permittedArrowDirection == MGLSMCalloutArrowDirectionUp)
+ insets.bottom -= size.height;
+ else
+ insets.top -= size.height;
+
+ return insets;
+}
+
+
- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToView:(UIView *)constrainedView animated:(BOOL)animated {
[self presentCalloutFromRect:rect inLayer:view.layer ofView:view constrainedToLayer:constrainedView.layer animated:animated];
}
@@ -255,8 +284,18 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
[self presentCalloutFromRect:rect inLayer:layer ofView:nil constrainedToLayer:constrainedLayer animated:animated];
}
-// this private method handles both CALayer and UIView parents depending on what's passed.
- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIView *)view constrainedToLayer:(CALayer *)constrainedLayer animated:(BOOL)animated {
+ // figure out the constrained view's rect in our popup view's coordinate system
+ CGRect constrainedRect = [constrainedLayer convertRect:constrainedLayer.bounds toLayer:layer];
+ [self presentCalloutFromRect:rect inLayer:layer ofView:view constrainedToRect:constrainedRect animated:animated];
+}
+
+- (void)presentCalloutFromRect:(CGRect)rect inView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {
+ [self presentCalloutFromRect:rect inLayer:view.layer ofView:view constrainedToRect:constrainedRect animated:animated];
+}
+
+// this private method handles both CALayer and UIView parents depending on what's passed.
+- (void)presentCalloutFromRect:(CGRect)rect inLayer:(CALayer *)layer ofView:(UIView *)view constrainedToRect:(CGRect)constrainedRect animated:(BOOL)animated {
// Sanity check: dismiss this callout immediately if it's displayed somewhere
if (self.layer.superlayer) [self dismissCalloutAnimated:NO];
@@ -265,8 +304,6 @@ NSTimeInterval const kMGLSMCalloutViewRepositionDelayForUIScrollView = 1.0/3.0;
[self.layer removeAnimationForKey:@"present"];
[self.layer removeAnimationForKey:@"dismiss"];
- // figure out the constrained view's rect in our popup view's coordinate system
- CGRect constrainedRect = [constrainedLayer convertRect:constrainedLayer.bounds toLayer:layer];
// apply our edge constraints
constrainedRect = UIEdgeInsetsInsetRect(constrainedRect, self.constrainedInsets);
diff --git a/platform/ios/vendor/mapbox-events-ios b/platform/ios/vendor/mapbox-events-ios
new file mode 160000
+Subproject ac14fe78043eb823f14c7eefacda29aad0642c9
diff --git a/platform/linux/README.md b/platform/linux/README.md
index 8b8ac9d089..a8a4d459ee 100644
--- a/platform/linux/README.md
+++ b/platform/linux/README.md
@@ -6,7 +6,7 @@ We are using Ubuntu for development. While the software should work on other dis
This process gives you a Linux desktop app built on a Linux host system.
-### Build
+### Prerequisites
Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you can also use [Clang 3.5+](http://llvm.org/apt/).
@@ -15,9 +15,6 @@ Install GCC 4.9+ if you are running Ubuntu 14.04 or older. Alternatively, you ca
sudo apt-get install gcc-4.9 g++-4.9
export CXX=g++-4.9
-**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).
-
Ensure you have git and other build essentials:
sudo apt-get install curl git build-essential zlib1g-dev automake \
@@ -38,6 +35,21 @@ Install glfw3 dependencies:
x11proto-xf86vidmode-dev libxxf86vm-dev \
libxcursor-dev libxinerama-dev
+[Node.js](https://nodejs.org/) 4.2.1 or later is also required.
+
+[ccache](https://ccache.samba.org) is optional, but improves recompilation performance.
+
+## Build
+
+Clone the git repository:
+
+ git clone https://github.com/mapbox/mapbox-gl-native.git
+ cd mapbox-gl-native
+
+Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command,
+but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to
+this repository.
+
Set the environment variable `MAPBOX_ACCESS_TOKEN` to your [Mapbox access token](ACCESS_TOKEN.md):
export MAPBOX_ACCESS_TOKEN=MYTOKEN
diff --git a/platform/linux/config.cmake b/platform/linux/config.cmake
index fddcf90278..53683daef0 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,8 +9,24 @@ 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)
+add_library(mbgl-loop-uv STATIC
+ platform/default/async_task.cpp
+ platform/default/run_loop.cpp
+ platform/default/timer.cpp
+)
+
+target_include_directories(mbgl-loop-uv
+ PRIVATE include
+ PRIVATE src
+)
+
+target_link_libraries(mbgl-loop-uv
+ PRIVATE mbgl-core
+)
+
+target_add_mason_package(mbgl-loop-uv PUBLIC libuv)
macro(mbgl_platform_core)
target_add_mason_package(mbgl-core PUBLIC mesa)
@@ -19,7 +34,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 +41,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 +78,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 +87,7 @@ macro(mbgl_platform_core)
target_include_directories(mbgl-core
PRIVATE platform/default
+ PRIVATE platform/linux
)
target_add_mason_package(mbgl-core PUBLIC nunicode)
@@ -114,12 +125,10 @@ macro(mbgl_platform_glfw)
PRIVATE mbgl-loop-uv
)
- target_add_mason_package(mbgl-glfw PUBLIC libuv)
-
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()
@@ -130,8 +139,6 @@ macro(mbgl_platform_render)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
-
- target_add_mason_package(mbgl-render PUBLIC libuv)
endmacro()
@@ -140,8 +147,6 @@ macro(mbgl_platform_offline)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
-
- target_add_mason_package(mbgl-offline PUBLIC libuv)
endmacro()
@@ -150,6 +155,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
@@ -160,8 +169,6 @@ macro(mbgl_platform_test)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
-
- target_add_mason_package(mbgl-test PUBLIC libuv)
endmacro()
@@ -180,8 +187,6 @@ macro(mbgl_platform_benchmark)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-uv
)
-
- target_add_mason_package(mbgl-benchmark PUBLIC libuv)
endmacro()
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..00d242211c 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -1,9 +1,75 @@
# Changelog for Mapbox Maps SDK for macOS
-## v0.6.2
+## master
+### Packaging
+
+* The minimum deployment target for this SDK is now macOS 10.11.0. ([#11776](https://github.com/mapbox/mapbox-gl-native/pull/11776))
+
+## 0.7.1
+
+### Style layers
+
+* Deprecated `+[NSExpression featurePropertiesVariableExpression]` use `+[NSExpression featureAttributesVariableExpression]` instead. ([#11748](https://github.com/mapbox/mapbox-gl-native/pull/11748))
+
+## 0.7.0 - April 19, 2018
+
+The 0.7._x_ series of releases will be the last to support macOS 10.10. The minimum macOS deployment version will increase to macOS 10.11.0 in a future release.
+
+### Packaging
+
+* Added Arabic, Danish, Hebrew, and European Portuguese localizations. ([#10967](https://github.com/mapbox/mapbox-gl-native/pull/10967), [#11136](https://github.com/mapbox/mapbox-gl-native/pull/11134), [#11695](https://github.com/mapbox/mapbox-gl-native/pull/11695))
+* Removed methods, properties, and constants that had been deprecated as of v0.6.1. ([#11205](https://github.com/mapbox/mapbox-gl-native/pull/11205))
+* Refined certain Swift interfaces by converting them from class methods to class properties. ([#11674](https://github.com/mapbox/mapbox-gl-native/pull/11674))
+
+### Style layers
+
+* 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, which include arithmetic and conditional operators. ([#10726](https://github.com/mapbox/mapbox-gl-native/pull/10726))
+* A style can now display a heatmap layer that visualizes a point data distribution. You can customize the appearance at runtime using the `MGLHeatmapStyleLayer` class. ([#11046](https://github.com/mapbox/mapbox-gl-native/pull/11046))
+* A style can now display a smooth hillshading layer 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))
+* You can now set the `MGLVectorStyleLayer.predicate` property to a predicate that contains arithmetic and calls to built-in `NSExpression` functions. You may need to cast a feature attribute key to `NSString` or `NSNumber` before comparing it to a string or number. ([#11587](https://github.com/mapbox/mapbox-gl-native/pull/11587))
+* Replaced the `MGLStyle.localizesLabels` property with an `-[MGLStyle localizeLabelsIntoLocale:]` method that allows you to specify the language to localize into. Note that this method does not automatically update the style when the system’s preferred language changes. Also added an `-[NSExpression(MGLAdditions) mgl_expressionLocalizedIntoLocale:]` method for localizing an individual value used with `MGLSymbolStyleLayer.text`. ([#11651](https://github.com/mapbox/mapbox-gl-native/pull/11651))
+* Fixed incorrect color calibration on macOS 10.13 High Sierra when using color-related methods of `MGLStyleLayer` subclasses, as well as when displaying an `MGLAttributionInfo`. It is no longer necessary to explicitly convert an `NSColor` to the sRGB color space before using these classes on High Sierra. ([#11391](https://github.com/mapbox/mapbox-gl-native/pull/11391))
+* The `MGLSymbolStyleLayer.textFontNames` property can now depend on a feature’s attributes. ([#10850](https://github.com/mapbox/mapbox-gl-native/pull/10850))
+* Changes to the `MGLStyleLayer.minimumZoomLevel` and `MGLStyleLayer.maximumZoomLevel` properties take effect immediately. ([#11399](https://github.com/mapbox/mapbox-gl-native/pull/11399))
+
+### Content sources
+
+* Renamed `MGLRasterSource` to `MGLRasterTileSource` and `MGLVectorSource` to `MGLVectorTileSource`. ([#11568](https://github.com/mapbox/mapbox-gl-native/pull/11568))
+* 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))
+* 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))
+
+### Map rendering
+
+* 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))
+* 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))
* Added the `MGLTileSourceOptionTileCoordinateBounds` option to create an `MGLTileSource` that only supplies tiles within a specific geographic bounding box. ([#11141](https://github.com/mapbox/mapbox-gl-native/pull/11141))
-* Fixed an issue that caused `-[MGLMapSnapshotter pointForCoordinate:]` to return the wrong point. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035))
+* 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))
+* Improved the sharpness of raster tiles on Retina displays. ([#10984](https://github.com/mapbox/mapbox-gl-native/pull/10984))
+* Fixed a crash parsing a malformed style. ([#11001](https://github.com/mapbox/mapbox-gl-native/pull/11001))
+* Fixed an issue where symbols with empty labels would always be hidden. ([#11206](https://github.com/mapbox/mapbox-gl-native/pull/11206))
+* Fixed an issue where a tilted map could flicker while displaying rotating symbols. ([#11488](https://github.com/mapbox/mapbox-gl-native/pull/11488))
+* Increased the maximum width of labels by a factor of two. ([#11508](https://github.com/mapbox/mapbox-gl-native/pull/11508))
+
+### Annotations
+
+* Fixed an issue where tapping a group of annotations may not have selected the nearest annotation. ([#11438](https://github.com/mapbox/mapbox-gl-native/pull/11438))
+* The `MGLMapView.selectedAnnotations` property (backed by `-[MGLMapView setSelectedAnnotations:]`) now selects annotations that are off-screen. ([#9790](https://github.com/mapbox/mapbox-gl-native/issues/9790))
+* The `animated` parameter to `-[MGLMapView selectAnnotation:animated:]` now controls whether the annotation and its callout are brought on-screen. If `animated` is `NO` then the annotation is selected if offscreen, but the map is not panned. Currently only point annotations are supported.([#3249](https://github.com/mapbox/mapbox-gl-native/issues/3249))
+* Fixed a crash when rapidly adding and removing annotations. ([#11551](https://github.com/mapbox/mapbox-gl-native/issues/11551), [#11575](https://github.com/mapbox/mapbox-gl-native/issues/11575))
+
+### Map snapshots
+
+* Fixed a memory leak that occurred when creating a map snapshot. ([#10585](https://github.com/mapbox/mapbox-gl-native/pull/10585))
+* Fixed an issue that caused `MGLMapSnapshotter.pointForCoordinate` to return an incorrect value. ([#11035](https://github.com/mapbox/mapbox-gl-native/pull/11035))
+
+### Other changes
+
+* The `-[MGLMapView convertRect:toCoordinateBoundsFromView:]` method and the `MGLMapView.visibleCoordinateBounds` property’s getter now indicate that the coordinate bounds straddles the antimeridian by extending one side beyond ±180 degrees longitude. ([#11265](https://github.com/mapbox/mapbox-gl-native/pull/11265))
+* Feature querying results now account for the `MGLSymbolStyleLayer.circleStrokeWidth` property. ([#10897](https://github.com/mapbox/mapbox-gl-native/pull/10897))
+* Reduced offline download sizes for styles with symbol layers that render only icons, and no text. ([#11055](https://github.com/mapbox/mapbox-gl-native/pull/11055))
## v0.6.1 - January 16, 2018
diff --git a/platform/macos/DEVELOPING.md b/platform/macos/DEVELOPING.md
index 562f1291c2..f4d946c527 100644
--- a/platform/macos/DEVELOPING.md
+++ b/platform/macos/DEVELOPING.md
@@ -4,24 +4,10 @@ This document explains how to build the Mapbox Maps SDK for macOS from source. I
## Requirements
-The Mapbox Maps SDK for macOS and the macosapp demo application run on macOS 10.10.0 or above.
-
-The Mapbox Maps SDK for macOS requires Xcode 8.0 or above.
+See the "Requirements" section in [INSTALL.md](INSTALL.md).
## Building the SDK
-1. [Install core dependencies](../../INSTALL.md).
-1. Run `make xproj`.
-1. Switch to the “dynamic” or “macosapp” scheme. The former builds just the Cocoa framework, while the latter also builds a Cocoa demo application based on it.
-
-### Packaging builds
-
-Install [jazzy](https://github.com/realm/jazzy) for generating API documentation:
-
-```bash
-[sudo] gem install jazzy
-```
-
Build and package the SDK by using one of the following commands:
* `make xpackage` builds a dynamic framework in the Debug configuration, including debug symbols.
@@ -117,6 +103,8 @@ To add an example code listing to the documentation for a class or class member:
to [MGLDocumentationExampleTests](test/MGLDocumentationExampleTests.swift).
Wrap the code you’d like to appear in the documentation within
`//#-example-code` and `//#-end-example-code` comments.
+1. If the header doesn’t already have an example code listing, add the path to
+ the header to platform/darwin/scripts/update-examples.list.
1. Insert the code listings into the headers:
```bash
diff --git a/platform/macos/INSTALL.md b/platform/macos/INSTALL.md
index c723d3e062..f0fb0278be 100644
--- a/platform/macos/INSTALL.md
+++ b/platform/macos/INSTALL.md
@@ -6,19 +6,41 @@ This document explains how to build a development version of the Mapbox Maps SDK
The Mapbox Maps SDK for macOS requires the macOS 10.10.0 SDK (or above) and Xcode 8.0 (or above). 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.
-### Building the SDK from source
-
-To build the SDK from source:
-
-1. [Install core dependencies](../../INSTALL.md).
+Before building, follow these steps to install prerequisites:
+1. Install [Xcode](https://developer.apple.com/xcode/)
+1. Launch Xcode and install any updates
+1. Install [Homebrew](http://brew.sh)
+1. Install [Node.js](https://nodejs.org/), [CMake](https://cmake.org/), and [ccache](https://ccache.samba.org):
+ ```
+ brew install node cmake ccache
+ ```
+1. Install [xcpretty](https://github.com/supermarin/xcpretty) (optional, used for prettifying command line builds):
+ ```
+ [sudo] gem install xcpretty
+ ```
1. Install [jazzy](https://github.com/realm/jazzy) for generating API documentation:
-
```
[sudo] gem install jazzy
```
-1. Run `make xpackage`, which produces a `Mapbox.framework` in the `build/macos/pkg/` folder.
+### Building the SDK from source
+
+To build the SDK from source:
+
+1. Clone the git repository:
+ ```
+ git clone https://github.com/mapbox/mapbox-gl-native.git
+ cd mapbox-gl-native
+ ```
+ Note that this repository uses Git submodules. They'll be automatically checked out when you first run a `make` command,
+ but are not updated automatically. We recommended that you run `git submodule update` after pulling down new commits to
+ this repository.
+1. Run:
+ ```
+ make xpackage
+ ```
+ This produces a `Mapbox.framework` in the `build/macos/pkg/` folder.
### Installation
diff --git a/platform/macos/Mapbox-macOS-SDK-symbols.podspec b/platform/macos/Mapbox-macOS-SDK-symbols.podspec
index f8b5397a77..2df93c6f42 100644
--- a/platform/macos/Mapbox-macOS-SDK-symbols.podspec
+++ b/platform/macos/Mapbox-macOS-SDK-symbols.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '0.6.1'
+ version = '0.7.0'
m.name = 'Mapbox-macOS-SDK-symbols'
m.version = "#{version}-symbols"
@@ -20,7 +20,7 @@ Pod::Spec.new do |m|
}
m.platform = :osx
- m.osx.deployment_target = '10.10'
+ m.osx.deployment_target = '10.11'
m.requires_arc = true
diff --git a/platform/macos/Mapbox-macOS-SDK.podspec b/platform/macos/Mapbox-macOS-SDK.podspec
index 6412190065..14b7426097 100644
--- a/platform/macos/Mapbox-macOS-SDK.podspec
+++ b/platform/macos/Mapbox-macOS-SDK.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |m|
- version = '0.6.1'
+ version = '0.7.0'
m.name = 'Mapbox-macOS-SDK'
m.version = version
@@ -20,7 +20,7 @@ Pod::Spec.new do |m|
}
m.platform = :osx
- m.osx.deployment_target = '10.10'
+ m.osx.deployment_target = '10.11'
m.requires_arc = true
diff --git a/platform/macos/app/AppDelegate.m b/platform/macos/app/AppDelegate.m
index 0d780424f9..f7b35d0c83 100644
--- a/platform/macos/app/AppDelegate.m
+++ b/platform/macos/app/AppDelegate.m
@@ -29,7 +29,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesCompleted {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfResourcesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
@@ -37,7 +37,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
return self.progress.countOfResourcesCompleted;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesExpected {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfResourcesExpected {
return [NSSet setWithObjects:@"progress", nil];
}
@@ -45,7 +45,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
return self.progress.countOfResourcesExpected;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfBytesCompleted {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfBytesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
@@ -53,7 +53,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
return self.progress.countOfBytesCompleted;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTilesCompleted {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfTilesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
@@ -61,7 +61,7 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
return self.progress.countOfTilesCompleted;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfTileBytesCompleted {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCountOfTileBytesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
@@ -157,11 +157,11 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
// mapboxgl://?center=29.95,-90.066667&zoom=14&bearing=45&pitch=30
NSURL *url = [NSURL URLWithString:[event paramDescriptorForKeyword:keyDirectObject].stringValue];
- NS_MUTABLE_DICTIONARY_OF(NSString *, NSString *) *params = [[NSMutableDictionary alloc] init];
+ NSMutableDictionary<NSString *, NSString *> *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url.query componentsSeparatedByString:@"&"]) {
NSArray *parts = [param componentsSeparatedByString:@"="];
if (parts.count >= 2) {
- params[parts[0]] = [parts[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ params[parts[0]] = [parts[1] stringByRemovingPercentEncoding];
}
}
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..8f0aeaf69c 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>
@@ -401,16 +402,6 @@
<action selector="showStyle:" target="-1" id="NTT-Y1-EqU"/>
</connections>
</menuItem>
- <menuItem title="Traffic Day" tag="7" keyEquivalent="7" id="m9S-sv-Dch">
- <connections>
- <action selector="showStyle:" target="-1" id="lXM-BW-dDw"/>
- </connections>
- </menuItem>
- <menuItem title="Traffic Night" tag="8" keyEquivalent="8" id="yVS-VL-Xsy">
- <connections>
- <action selector="showStyle:" target="-1" id="eAD-br-oBB"/>
- </connections>
- </menuItem>
<menuItem title="Custom Style…" id="L0h-86-2cU">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -554,6 +545,27 @@
<action selector="drawAnimatedAnnotation:" target="-1" id="CYM-WB-s97"/>
</connections>
</menuItem>
+ <menuItem title="Select an Offscreen Point Annotation" id="Xy2-Cc-RUB">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="selectOffscreenPointAnnotation:" target="-1" id="Fhm-l3-G6h"/>
+ </connections>
+ </menuItem>
+ <menuItem title="Show All Annotations" keyEquivalent="A" id="yMj-uM-8SN">
+ <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
+ <connections>
+ <action selector="showAllAnnotations:" target="-1" id="ahr-OR-Em2"/>
+ </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 +578,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 +670,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="84"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="84"/>
<autoresizingMask key="autoresizingMask"/>
@@ -736,7 +746,7 @@ CA
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="830" y="430" width="400" height="300"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
@@ -744,11 +754,11 @@ CA
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
<rect key="frame" x="-1" y="20" width="402" height="281"/>
<clipView key="contentView" id="J9U-Yx-o2S">
- <rect key="frame" x="1" y="0.0" width="400" height="280"/>
- <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <rect key="frame" x="1" y="0.0" width="400" height="265"/>
+ <autoresizingMask key="autoresizingMask"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
- <rect key="frame" x="0.0" y="0.0" width="423" height="257"/>
+ <rect key="frame" x="0.0" y="0.0" width="423" height="242"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@@ -766,7 +776,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 +796,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 +830,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 +851,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"/>
@@ -887,7 +897,7 @@ CA
</subviews>
</clipView>
<scroller key="horizontalScroller" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
- <rect key="frame" x="1" y="264" width="400" height="16"/>
+ <rect key="frame" x="1" y="265" width="400" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
diff --git a/platform/macos/app/Base.lproj/MapDocument.xib b/platform/macos/app/Base.lproj/MapDocument.xib
index 0394f38533..5d0525a29d 100644
--- a/platform/macos/app/Base.lproj/MapDocument.xib
+++ b/platform/macos/app/Base.lproj/MapDocument.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">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13771" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<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>
@@ -48,7 +49,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="388" y="211" width="642" height="480"/>
- <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="TuG-C5-zLS">
<rect key="frame" x="0.0" y="0.0" width="642" height="480"/>
<autoresizingMask key="autoresizingMask"/>
@@ -70,7 +71,7 @@
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
- <tableColumn editable="NO" width="16" minWidth="16" maxWidth="1000" id="P3U-a3-c8q">
+ <tableColumn identifier="" editable="NO" width="16" minWidth="16" maxWidth="1000" id="P3U-a3-c8q">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -87,7 +88,7 @@
</binding>
</connections>
</tableColumn>
- <tableColumn editable="NO" width="141" minWidth="40" maxWidth="1000" id="BwD-ww-7uw">
+ <tableColumn identifier="" editable="NO" width="141" minWidth="40" maxWidth="1000" id="BwD-ww-7uw">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
@@ -204,12 +205,6 @@
<menuItem title="Satellite Streets" tag="6" id="7ly-oA-0ND">
<modifierMask key="keyEquivalentModifierMask"/>
</menuItem>
- <menuItem title="Traffic Day" tag="7" id="Vz7-9Z-EFq">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Traffic Night" tag="8" id="zh7-LM-dmF">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
</items>
</menu>
</popUpButtonCell>
diff --git a/platform/macos/app/MGLStyle+MBXAdditions.h b/platform/macos/app/MGLStyle+MBXAdditions.h
index dcaf42af28..29c2cda867 100644
--- a/platform/macos/app/MGLStyle+MBXAdditions.h
+++ b/platform/macos/app/MGLStyle+MBXAdditions.h
@@ -2,6 +2,6 @@
@interface MGLStyle (MBXAdditions)
-@property (nonatomic, strong) NS_ARRAY_OF(__kindof MGLStyleLayer *) *reversedLayers;
+@property (nonatomic, strong) NSArray<__kindof MGLStyleLayer *> *reversedLayers;
@end
diff --git a/platform/macos/app/MGLStyle+MBXAdditions.m b/platform/macos/app/MGLStyle+MBXAdditions.m
index be571d8b30..a0773cc2c0 100644
--- a/platform/macos/app/MGLStyle+MBXAdditions.m
+++ b/platform/macos/app/MGLStyle+MBXAdditions.m
@@ -2,15 +2,15 @@
@implementation MGLStyle (MBXAdditions)
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingReversedLayers {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingReversedLayers {
return [NSSet setWithObject:@"layers"];
}
-- (NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers {
+- (NSArray<__kindof MGLStyleLayer *> *)reversedLayers {
return self.layers.reverseObjectEnumerator.allObjects;
}
-- (void)setReversedLayers:(NS_ARRAY_OF(__kindof MGLStyleLayer *) *)reversedLayers {
+- (void)setReversedLayers:(NSArray<__kindof MGLStyleLayer *> *)reversedLayers {
self.layers = reversedLayers.reverseObjectEnumerator.allObjects;
}
diff --git a/platform/macos/app/MapDocument.m b/platform/macos/app/MapDocument.m
index 0df6b10518..7be500ae6a 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 "MGLVectorTileSource_Private.h"
#import <Mapbox/Mapbox.h>
@@ -19,7 +19,7 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
{ .latitude = -13.15589555, .longitude = -74.2178961777998 },
};
-NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotation>) *shapes) {
+NSArray<id <MGLAnnotation>> *MBXFlattenedShapes(NSArray<id <MGLAnnotation>> *shapes) {
NSMutableArray *flattenedShapes = [NSMutableArray arrayWithCapacity:shapes.count];
for (id <MGLAnnotation> shape in shapes) {
NSArray *subshapes;
@@ -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 MGLVectorTileSource (MBXAdditions)
+
+@property (nonatomic, readonly, getter=isMapboxTerrain) BOOL mapboxTerrain;
+
+@end
+
+@implementation MGLVectorTileSource (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;
@@ -246,12 +265,6 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
case 6:
styleURL = [MGLStyle satelliteStreetsStyleURL];
break;
- case 7:
- styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"];
- break;
- case 8:
- styleURL = [NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"];
- break;
default:
NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
break;
@@ -327,7 +340,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)toggleStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices {
- NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices];
+ NSArray<MGLStyleLayer *> *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices];
BOOL isVisible = layers.firstObject.visible;
[self.undoManager registerUndoWithTarget:self handler:^(MapDocument * _Nonnull target) {
[target toggleStyleLayersAtArrangedObjectIndexes:indices];
@@ -363,7 +376,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[self deleteStyleLayersAtArrangedObjectIndexes:indices];
}
-- (void)insertStyleLayers:(NS_ARRAY_OF(MGLStyleLayer *) *)layers atArrangedObjectIndexes:(NSIndexSet *)indices {
+- (void)insertStyleLayers:(NSArray<MGLStyleLayer *> *)layers atArrangedObjectIndexes:(NSIndexSet *)indices {
[self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
[self deleteStyleLayersAtArrangedObjectIndexes:indices];
}];
@@ -383,7 +396,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)deleteStyleLayersAtArrangedObjectIndexes:(NSIndexSet *)indices {
- NS_ARRAY_OF(MGLStyleLayer *) *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices];
+ NSArray<MGLStyleLayer *> *layers = [self.mapView.style.reversedLayers objectsAtIndexes:indices];
[self.undoManager registerUndoWithTarget:self handler:^(id _Nonnull target) {
[self insertStyleLayers:layers atArrangedObjectIndexes:indices];
}];
@@ -408,7 +421,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
}
- (void)updateLabels {
- self.mapView.style.localizesLabels = _isLocalizingLabels;
+ [self.mapView.style localizeLabelsIntoLocale:_isLocalizingLabels ? nil : [NSLocale localeWithLocaleIdentifier:@"mul"]];
}
- (void)applyPendingState {
@@ -559,7 +572,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[self continueWorldTourWithRemainingAnnotations:annotations];
}
-- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations {
+- (void)continueWorldTourWithRemainingAnnotations:(NSMutableArray<MGLPointAnnotation *> *)annotations {
MGLPointAnnotation *nextAnnotation = annotations.firstObject;
if (!nextAnnotation || !_isTouringWorld) {
_isTouringWorld = NO;
@@ -628,6 +641,51 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
repeats:YES];
}
+
+- (id<MGLAnnotation>)randomOffscreenPointAnnotation {
+
+ NSPredicate *pointAnnotationPredicate = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
+ return [evaluatedObject isKindOfClass:[MGLPointAnnotation class]];
+ }];
+
+ NSArray *annotations = [self.mapView.annotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ if (annotations.count == 0) {
+ return nil;
+ }
+
+ // NOTE: self.mapView.visibleAnnotations occasionally returns nil - see
+ // https://github.com/mapbox/mapbox-gl-native/issues/11296
+ NSArray *visibleAnnotations = [self.mapView.visibleAnnotations filteredArrayUsingPredicate:pointAnnotationPredicate];
+
+ NSLog(@"Number of visible point annotations = %ld", visibleAnnotations.count);
+
+ if (visibleAnnotations.count == annotations.count) {
+ return nil;
+ }
+
+ NSMutableArray *invisibleAnnotations = [annotations mutableCopy];
+
+ if (visibleAnnotations.count > 0) {
+ [invisibleAnnotations removeObjectsInArray:visibleAnnotations];
+ }
+
+ // Now pick a random offscreen annotation.
+ uint32_t index = arc4random_uniform((uint32_t)invisibleAnnotations.count);
+ return invisibleAnnotations[index];
+}
+
+- (IBAction)selectOffscreenPointAnnotation:(id)sender {
+ id<MGLAnnotation> annotation = [self randomOffscreenPointAnnotation];
+ if (annotation) {
+ [self.mapView selectAnnotation:annotation];
+
+ // Alternative method to select the annotation. These two should do the same thing.
+ // self.mapView.selectedAnnotations = @[annotation];
+ NSAssert(self.mapView.selectedAnnotations.firstObject, @"The annotation was not selected");
+ }
+}
+
- (void)updateAnimatedAnnotation:(NSTimer *)timer {
DroppedPinAnnotation *annotation = timer.userInfo;
double angle = timer.fireDate.timeIntervalSinceReferenceDate;
@@ -698,6 +756,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 (MGLVectorTileSource *source in self.mapView.style.sources) {
+ if (![source isKindOfClass:[MGLVectorTileSource 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 +894,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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, '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 +912,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 +925,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:@"mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)", @{
+ @16.0: [NSColor redColor],
+ @18.0: [NSColor yellowColor],
+ @20.0: [NSColor blackColor],
+ }];
[self.mapView.style addLayer:theaterLayer];
}
@@ -872,12 +1020,6 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
case 6:
state = [styleURL isEqual:[MGLStyle satelliteStreetsStyleURL]];
break;
- case 7:
- state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-day-v2"]];
- break;
- case 8:
- state = [styleURL isEqual:[NSURL URLWithString:@"mapbox://styles/mapbox/traffic-night-v2"]];
- break;
default:
return NO;
}
@@ -925,7 +1067,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
menuItem.state = menuItem.tag == _isLocalizingLabels ? NSOnState: NSOffState;
if (menuItem.tag) {
NSLocale *locale = [NSLocale localeWithLocaleIdentifier:[NSBundle mainBundle].developmentLocalization];
- NSString *preferredLanguage = [MGLVectorSource preferredMapboxStreetsLanguage];
+ NSString *preferredLanguage = [MGLVectorTileSource preferredMapboxStreetsLanguage] ?: @"en";
menuItem.title = [locale displayNameForKey:NSLocaleIdentifier value:preferredLanguage];
}
return YES;
@@ -1011,9 +1153,18 @@ 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(selectOffscreenPointAnnotation:)) {
+ return YES;
+ }
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;
}
@@ -1045,8 +1196,6 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
[MGLStyle darkStyleURL],
[MGLStyle satelliteStyleURL],
[MGLStyle satelliteStreetsStyleURL],
- [MGLStyle trafficDayStyleURL],
- [MGLStyle trafficNightStyleURL],
];
return [styleURLs indexOfObject:self.mapView.styleURL];
}
@@ -1087,7 +1236,7 @@ NS_ARRAY_OF(id <MGLAnnotation>) *MBXFlattenedShapes(NS_ARRAY_OF(id <MGLAnnotatio
#pragma mark NSSharingServicePickerDelegate methods
-- (NS_ARRAY_OF(NSSharingService *) *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NS_ARRAY_OF(NSSharingService *) *)proposedServices {
+- (NSArray<NSSharingService *> *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NSArray<NSSharingService *> *)proposedServices {
NSURL *shareURL = self.shareURL;
NSURL *browserURL = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:shareURL];
NSImage *browserIcon = [[NSWorkspace sharedWorkspace] iconForFile:browserURL.path];
@@ -1185,6 +1334,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/da.lproj/Localizable.strings b/platform/macos/app/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/da.lproj/Localizable.strings
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/pt-PT.lproj/Localizable.strings b/platform/macos/app/pt-PT.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/platform/macos/app/pt-PT.lproj/Localizable.strings
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..28573258d9 100644
--- a/platform/macos/config.cmake
+++ b/platform/macos/config.cmake
@@ -1,12 +1,11 @@
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)
macro(mbgl_platform_core)
@@ -34,8 +33,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 +51,6 @@ macro(mbgl_platform_core)
target_compile_options(mbgl-core
PRIVATE -fobjc-arc
- PRIVATE -fvisibility=hidden
)
target_include_directories(mbgl-core
@@ -86,7 +82,6 @@ macro(mbgl_filesource)
target_compile_options(mbgl-filesource
PRIVATE -fobjc-arc
- PRIVATE -fvisibility=hidden
)
target_link_libraries(mbgl-filesource
@@ -101,11 +96,6 @@ macro(mbgl_platform_glfw)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
-
- target_compile_options(mbgl-glfw
- PRIVATE -fvisibility=hidden
- )
-
endmacro()
@@ -114,9 +104,6 @@ macro(mbgl_platform_render)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
- target_compile_options(mbgl-render
- PRIVATE -fvisibility=hidden
- )
endmacro()
@@ -125,9 +112,6 @@ macro(mbgl_platform_offline)
PRIVATE mbgl-filesource
PRIVATE mbgl-loop-darwin
)
- target_compile_options(mbgl-offline
- PRIVATE -fvisibility=hidden
- )
endmacro()
@@ -136,16 +120,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 +137,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 +154,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/doc-README.md b/platform/macos/docs/doc-README.md
index 8089ae17d1..8ce2df39a4 100644
--- a/platform/macos/docs/doc-README.md
+++ b/platform/macos/docs/doc-README.md
@@ -1,6 +1,6 @@
# [Mapbox Maps SDK for macOS](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/macos/)
-The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.10.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
+The Mapbox Maps SDK for macOS is an open-source framework for embedding interactive map views with scalable, customizable vector maps into Cocoa applications on macOS 10.11.0 and above using Objective-C, Swift, Interface Builder, or AppleScript. The SDK takes stylesheets that conform to the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/), applies them to vector tiles that conform to the [Mapbox Vector Tile Specification](https://www.mapbox.com/developers/vector-tiles/), and renders them using OpenGL.
![](img/screenshot.jpg)
diff --git a/platform/macos/docs/guides/For Style Authors.md b/platform/macos/docs/guides/For Style Authors.md
index d1b08f74a9..22182c7810 100644
--- a/platform/macos/docs/guides/For Style Authors.md
+++ b/platform/macos/docs/guides/For Style Authors.md
@@ -114,21 +114,22 @@ source object is a member of one of the following subclasses of `MGLSource`:
In style JSON | In the SDK
--------------|-----------
+`vector` | `MGLVectorTileSource`
+`raster` | `MGLRasterTileSource`
+`raster-dem` | `MGLRasterDEMSource`
`geojson` | `MGLShapeSource`
-`raster` | `MGLRasterSource`
-`vector` | `MGLVectorSource`
`image` | `MGLImageSource`
`canvas` and `video` sources are not supported.
### Tile sources
-Raster and vector sources may be defined in TileJSON configuration files. This
-SDK supports the properties defined in the style specification, which are a
+Raster and vector tile sources may be defined in TileJSON configuration files.
+This SDK supports the properties defined in the style specification, which are a
subset of the keys defined in version 2.1.0 of the
[TileJSON](https://github.com/mapbox/tilejson-spec/tree/master/2.1.0)
specification. As an alternative to authoring a custom TileJSON file, you may
-supply various tile source options when creating a raster or vector source.
+supply various tile source options when creating a raster or vector tile source.
These options are detailed in the `MGLTileSourceOption` documentation:
In style JSON | In TileJSON | In the SDK
@@ -141,6 +142,7 @@ In style JSON | In TileJSON | In the SDK
`tileSize` | — | `MGLTileSourceOptionTileSize`
`attribution` | `attribution` | `MGLTileSourceOptionAttributionHTMLString` (but consider specifying `MGLTileSourceOptionAttributionInfos` instead for improved security)
`scheme` | `scheme` | `MGLTileSourceOptionTileCoordinateSystem`
+`encoding` | – | `MGLTileSourceOptionDEMEncoding`
### Shape sources
@@ -179,6 +181,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 +262,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 +282,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,42 +303,88 @@ 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
+
+Many expression operators defined in the style specification have corresponding
+symbols to be used with the `+[NSExpression expressionWithFormat:]`,
+`+[NSExpression expressionForFunction:arguments:]`, or
+`+[NSExpression expressionForFunction:selectorName:arguments:]` method:
+
+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:` | `CAST(zipCode, 'NSNumber')`
+`to-string` | `stringValue` | `CAST(ele, 'NSString')`
+`typeof` | |
+`geometry-type` | `NSExpression.geometryTypeVariableExpression` | `$geometryType`
+`id` | `NSExpression.featureIdentifierVariableExpression` | `$featureIdentifier`
+`properties` | `NSExpression.featureAttributesVariableExpression` | `$featureAttributes`
+`at` | `objectFrom:withIndex:` | `array[n]`
+`get` | `+[NSExpression expressionForKeyPath:]` | Key path
+`has` | `mgl_does:have:` | `mgl_does:have:(self, 'key')`
+`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:]` or `MGL_IF` or `+[NSExpression mgl_expressionForConditional:trueExpression:falseExpresssion:]` | `TERNARY(1 = 2, YES, NO)` or `MGL_IF(1 = 2, YES, 2 = 2, YES, NO)`
+`coalesce` | `mgl_coalesce:` | `mgl_coalesce({x, y, z})`
+`match` | `MGL_MATCH` or `+[NSExpression mgl_expressionForMatchingExpression:inDictionary:defaultExpression:]` | `MGL_MATCH(x, 0, 'zero match', 1, 'one match', 'two match', 'default')`
+`interpolate` | `mgl_interpolate:withCurveType:parameters:stops:` or `+[NSExpression mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` |
+`step` | `mgl_step:withMinimum:stops:` or `+[NSExpression mgl_expressionForSteppingExpression:fromExpression:stops:]` |
+`let` | `mgl_expressionWithContext:` | `MGL_LET('ios', 11, 'macos', 10.13, $ios + $macos)`
+`var` | `+[NSExpression expressionForVariable:]` | `$variable`
+`concat` | `mgl_join:` or `-[NSExpression mgl_expressionByAppendingExpression:]` | `mgl_join({'Old', ' ', 'MacDonald'})`
+`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`
+`abs` | `abs:` | `abs(-1)`
+`acos` | `mgl_acos:` | `mgl_acos(1)`
+`asin` | `mgl_asin:` | `mgl_asin(0)`
+`atan` | `mgl_atan:` | `mgl_atan(20)`
+`ceil` | `ceiling:` | `ceiling(0.99999)`
+`cos` | `mgl_cos:` | `mgl_cos(0)`
+`e` | | `%@` representing `NSNumber` containing `M_E`
+`floor` | `floor:` | `floor(-0.99999)`
+`ln` | `ln:` | `ln(2)`
+`ln2` | | `%@` representing `NSNumber` containing `M_LN2`
+`log10` | `log:` | `log(1)`
+`log2` | `mgl_log2:` | `mgl_log2(1024)`
+`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`
+`round` | `mgl_round:` | `mgl_round(1.5)`
+`sin` | `mgl_sin:` | `mgl_sin(0)`
+`sqrt` | `sqrt:` | `sqrt(2)`
+`tan` | `mgl_tan:` | `mgl_tan(0)`
+`zoom` | `NSExpression.zoomLevelVariableExpression` | `$zoom`
+`heatmap-density` | `NSExpression.heatmapDensityVariableExpression` | `$heatmapDensity`
+
+For operators that have no corresponding `NSExpression` symbol, use the
+`MGL_FUNCTION()` format string syntax.
## Filtering sources
-You can filter a shape or vector source by setting the
+You can filter a shape or vector tile source by setting the
`MGLVectorStyleLayer.predicate` property to an `NSPredicate` object. Below is a
table of style JSON operators and the corresponding operators used in the
predicate format string:
@@ -351,5 +405,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/Info.plist Keys.md b/platform/macos/docs/guides/Info.plist Keys.md
index be6096d167..dd401d1ad3 100644
--- a/platform/macos/docs/guides/Info.plist Keys.md
+++ b/platform/macos/docs/guides/Info.plist Keys.md
@@ -8,7 +8,7 @@ Set the [Mapbox access token](https://www.mapbox.com/help/define-access-token/)
Mapbox-hosted vector tiles and styles require an API access token, which you can obtain from the [Mapbox account page](https://www.mapbox.com/studio/account/tokens/). Access tokens associate requests to Mapbox’s vector tile and style APIs with your Mapbox account. They also deter other developers from using your styles without your permission.
-As an alternative, you can use `+[MGLAccountManager setAccessToken:]` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private.
+As an alternative, you can use `MGLAccountManager.accessToken` to set a token in code. See [our guide](https://www.mapbox.com/help/ios-private-access-token/) for some tips on keeping access tokens in open source code private.
## MGLMapboxAPIBaseURL
diff --git a/platform/macos/docs/guides/Migrating to Expressions.md b/platform/macos/docs/guides/Migrating to Expressions.md
new file mode 100644
index 0000000000..e8d038dbb0
--- /dev/null
+++ b/platform/macos/docs/guides/Migrating to Expressions.md
@@ -0,0 +1,267 @@
+<!--
+ This file is generated.
+ Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
+-->
+
+# Migrating from Style Functions to Expressions
+
+[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Developers can specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
+
+With Mapbox Maps SDK for macOS v0.7.0, style functions have been replaced with expressions. These provide even more tools for developers who want to style their maps dynamically. This guide outlines some tips for migrating from style functions to expressions, and offers an overview of some things that developers can do with expressions.
+
+An expression is represented at runtime by the `NSExpression` class. Expressions can be used to style paint and layout properties based on zoom level, data attributes, or a combination of the two.
+
+A constant expression can also be assigned to a style property. For example, the opacity of a fill style layer can be set to a constant value between 0 and 1.
+
+The documentation for each individual style layer property notes which non-constant expressions are enabled for that property. Style functions supported four interpolation modes: exponential, interval, categorical, and identity.
+
+This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php). Under each interpolation mode, the style function implementation will be shown, followed by the current syntax.
+
+For more information about how to work with GeoJSON data in our macOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide. To learn more about supported expressions, see our ["Predicates and Expressions"](predicates-and-expressions.html) guide. The "Predicates and Expressions" guide also outlines Mapbox custom functions that can be used to dynamically style a map.
+
+## Stops
+Stops are dictionary keys that are associated with layer attribute values. Constant values no longer need to be wrapped as style values when they are values in a stops dictionary.
+
+
+Style function syntax:
+
+```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),
+]
+```
+
+Current syntax:
+```swift
+let stops: [NSNumber: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+```
+
+
+## Interpolation mode
+
+Style functions supported four interpolation modes: exponential/linear, interval, categorical, and identity. For more information about supported custom expressions, please see the "Predicates and Expressions" guide.
+
+### Linear
+
+`+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` takes the interpolation type as a parameter. If you previously used the default interpolation base, use the curve type `MGLExpressionInterpolationMode.linear`. See the [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code) documentation for more details.
+
+The stops dictionary below, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
+
+Style function syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+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 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)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+Current syntax:
+
+```swift
+let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
+let symbolSource = MGLSource(identifier: "source")
+let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
+
+let source = MGLShapeSource(identifier: "earthquakes", url: url, options: nil)
+mapView.style?.addSource(source)
+
+let stops: [NSNumber: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+
+let layer = MGLCircleStyleLayer(identifier: "circles", source: source)
+layer.circleColor = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:(mag, 'linear', nil, %@)",
+ stops)
+layer.circleRadius = NSExpression(forConstantValue: 10)
+mapView.style?.insertLayer(layer, below: symbolLayer)
+```
+
+### Exponential
+
+If you previously used an interpolation base greater than `0` (other than `1`), you can use `MGLExpressionInterpolationMode.exponential` as the curve type for `+[NSExpression(MGLAdditions) mgl_expressionForInterpolatingExpression:withCurveType:parameters:stops:]` or `'exponential'` as the curve type for [`mgl_interpolate:withCurveType:parameters:stops:`](predicates-and-expressions.html#code-mgl_interpolate-withcurvetype-parameters-stops-code). The `parameters` argument takes that interpolation base. This interpolates between values exponentially, creating an accelerated ramp effect.
+
+Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom. In order to convert camera style functions, use `$zoomLevel` or `MGL_FUNCTION('zoomLevel')` as the attribute key.
+
+<img src="img/data-driven-styling/exponential-function.png" height=344/>
+<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
+
+The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The interpolation base is `1.5`.
+
+Style function syntax:
+
+```swift
+let stops = [
+ 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
+ 14: MGLStyleValue(rawValue: 2),
+ 18: MGLStyleValue(rawValue: 18),
+]
+
+layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
+ cameraStops: stops,
+ options: [.interpolationBase: 1.5])
+```
+
+Current syntax:
+
+```swift
+let stops = [
+ 12: 0.5,
+ 14: 2,
+ 18: 18,
+]
+
+layer.circleRadius = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'exponential', 1.5, %@)",
+ stops)
+```
+
+### Interval
+
+Steps, or intervals, create a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the layout or paint value assigned to that key. You can use the `+[NSExpression(MGLAdditions) mgl_expressionForSteppingExpression:fromExpression:stops:]` method or the custom function [`mgl_step:from:stops:`](predicates-and-expressions.html#code-mgl_step-from-stops-code) for cases where you previously used interval interpolation mode. The first parameter takes the feature attribute name and the second parameter (`from:`) optionally takes the default or fallback value for that function. The final parameter takes a stops dictionary as an argument.
+
+When we use the stops dictionary given above with an `'mgl_step:from:stops:'`, 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.
+
+Style function syntax:
+
+```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),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .interval,
+ sourceStops: stops,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .green)])
+````
+
+Current syntax:
+
+```swift
+let stops: [NSNumber: NSColor] = [
+ 0: .yellow,
+ 2.5: .orange,
+ 5: .red,
+ 7.5: .blue,
+ 10: .white,
+]
+
+layer.circleColor = NSExpression(format: "mgl_step:from:stops:(mag, %@, %@)",
+ NSColor.green, stops)
+```
+
+### Categorical
+
+Categorical interpolation mode took a stops dictionary. If the value for a specified feature attribute name matched one in that stops dictionary, the style value for that attribute value would be used. Categorical style functions can now be replaced with `MGL_MATCH`.
+
+`MGL_MATCH` takes an initial condition, which in this case is an attribute key. This is followed by possible matches for that key and the value to assign to the layer property if there is a match. The final argument can be a default style value that is to be used if none of the specified values match.
+
+There are three main types of events in the USGS 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.
+
+Style function syntax:
+
+```swift
+let categoricalStops = [
+ "earthquake": MGLStyleValue<UIColor>(rawValue: .orange),
+ "explosion": MGLStyleValue(rawValue: .red),
+ "quarry blast": MGLStyleValue(rawValue: .yellow),
+]
+
+layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
+ sourceStops: categoricalStops,
+ attributeName: "type",
+ options: [.defaultValue: MGLStyleValue<UIColor>(rawValue: .blue)])
+```
+
+Current syntax:
+```swift
+let defaultColor = NSColor.blue
+layer.circleColor = NSExpression(
+format: "MGL_MATCH(type, 'earthquake', %@, 'explosion', %@, 'quarry blast', %@, %@)",
+ NSColor.orange, NSColor.red, NSColor.yellow, defaultColor)
+```
+
+If your use case does not require a default value, you can either apply a predicate to your layer prior to styling it, or use the format string `"valueForKeyPath:"`.
+
+### Identity
+
+Identity interpolation mode used the attribute’s value as the style layer property value. In this example, you might set the `circleRadius` to the earthquake’s magnitude. In order to use a feature attribute value to style a layer property, set the property value to `[NSExpression expressionForKeyPath:]`, which take the feature attribute name as an argument.
+
+Style function syntax:
+
+```swift
+layer.circleRadius = MGLStyleValue(interpolationMode: .identity,
+ sourceStops: nil,
+ attributeName: "mag",
+ options: [.defaultValue: MGLStyleValue<NSNumber>(rawValue: 0)])
+```
+
+Current syntax:
+```swift
+layer.circleRadius = NSExpression(forKeyPath: "mag")
+```
+
+![identity mode](img/data-driven-styling/identity.png)
+
+Some built-in functions can be applied to attribute values to style layer property values. To set the circle radius to three times the earthquake’s magnitude, create a `multiply:by:` function that takes the attribute value and the multiplier as arguments, or use a format string.
+
+```swift
+layer.circleRadius = NSExpression(forFunction: "multiply:by:", arguments: [NSExpression(forKeyPath: "mag"), 3])
+```
+
+![multiply magnitude](img/data-driven-styling/multiply.png)
+
+You can also cast attribute values in order to use them. One example is to cast an integer as an `NSString` and use it as a text value.
+
+```swift
+let magnitudeLayer = MGLSymbolStyleLayer(identifier: "mag-layer", source: source)
+magnitudeLayer.text = NSExpression(format: "CAST(mag, 'NSString')")
+mapView.style?.addLayer(magnitudeLayer)
+```
+
+![cast a value](img/data-driven-styling/cast.png)
+
+### Constant Values
+
+For constant values that do not necessarily change based on camera or attribute values, use `[NSExpression expressionForConstantValue:]` (previously `[MGLStyleValue valueWithRawValue:]`).
+
+## Resources
+
+* [USGS Earthquake Feed](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
+* [For Style Authors](for-style-authors.html)
+* [Predicates and Expressions](predicates-and-expressions.html)
diff --git a/platform/macos/docs/guides/Tile URL Templates.md b/platform/macos/docs/guides/Tile URL Templates.md
index 01672c6686..e71bcab0a4 100644
--- a/platform/macos/docs/guides/Tile URL Templates.md
+++ b/platform/macos/docs/guides/Tile URL Templates.md
@@ -4,12 +4,12 @@
-->
# Tile URL Templates
-`MGLTileSource` objects, specifically `MGLRasterSource` and `MGLVectorSource`
-objects, can be created using an initializer that accepts an array of tile URL
-templates. Tile URL templates are strings that specify the URLs of the vector
-tiles or raster tile images to load. A template resembles an absolute URL, but
-with any number of placeholder strings that the source evaluates based on the
-tile it needs to load. For example:
+`MGLTileSource` objects, specifically `MGLRasterTileSource` and
+`MGLVectorTileSource` objects, can be created using an initializer that accepts
+an array of tile URL templates. Tile URL templates are strings that specify the
+URLs of the vector tiles or raster tile images to load. A template resembles an
+absolute URL, but with any number of placeholder strings that the source
+evaluates based on the tile it needs to load. For example:
* `http://www.example.com/tiles/{z}/{x}/{y}.pbf` could be
evaluated as `http://www.example.com/tiles/14/6/9.pbf`.
@@ -56,7 +56,7 @@ all of which are optional:
<td>The tile’s zoom level. At zoom level 0, each tile covers the entire
world map; at zoom level 1, it covers ¼ of the world; at zoom level 2,
<sup>1</sup>⁄<sub>16</sub> of the world, and so on. For tiles loaded by
- a <code>MGLRasterSource</code> object, whether the tile zoom level
+ a <code>MGLRasterTileSource</code> object, whether the tile zoom level
matches the map’s current zoom level depends on the value of the
source’s tile size as specified in the
<code>MGLTileSourceOptionTileSize</code> key of the <code>options</code>
diff --git a/platform/macos/docs/guides/Using Style Functions at Runtime.md b/platform/macos/docs/guides/Using Style Functions at Runtime.md
deleted file mode 100644
index ea772a93a2..0000000000
--- a/platform/macos/docs/guides/Using Style Functions at Runtime.md
+++ /dev/null
@@ -1,162 +0,0 @@
-<!--
- This file is generated.
- Edit platform/darwin/scripts/generate-style-code.js, then run `make darwin-style-code`.
--->
-
-# Using Style Functions at Runtime
-
-[Runtime Styling](runtime-styling.html) enables you to modify every aspect of the map’s appearance dynamically as a user interacts with your application. Much of the runtime styling API allows you to specify _style functions_ instead of constant values. A style function allows you to specify in advance how a layout or paint attribute will vary as the zoom level changes or how the appearance of individual features vary based on metadata provided by a content source.
-
-Style functions spare you the inconvenience of manually calculating intermediate values between different zoom levels or creating a multitude of style layers to handle homogeneous features in the map content. For example, if your content source indicates the prices of hotels in an area, you can color-code the hotels by price, relying on a style function to smoothly interpolate among desired colors without having to specify the color for each exact price.
-
-_Data-driven styling_ specifically refers to the use of style functions to vary the map’s appearance based on data in a content source.
-
-You can also specify style functions in a style JSON file, to be applied automatically when the map loads. See the [Mapbox Style Specification](https://www.mapbox.com/mapbox-gl-js/style-spec/#types-function) for details.
-
-![available bikes](img/data-driven-styling/citibikes.png) ![subway lines](img/data-driven-styling/polylineExample.png)
-
-This guide uses earthquake data from the [U.S. Geological Survey](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php) and data-driven styling to style a map based on attributes. For more information about how to work with GeoJSON data in our iOS SDK, please see our [working with GeoJSON data](working-with-geojson-data.html) guide.
-
-A style function is represented at runtime by the `MGLStyleFunction` class. There are three subclasses of `MGLStyleFunction`:
-
-* `MGLCameraStyleFunction` is a style value that changes with zoom level. For example, you can make the radius of a circle increase according to zoom level.
-* `MGLSourceStyleFunction` is a style value that changes with the attributes of a feature. For example, you can adjust the radius of a circle based on the magnitude of an earthquake.
-* `MGLCompositeStyleFunction` is a style value that changes with both zoom level and attribute values. For example, you can add a circle layer where each circle has a radius based on both zoom level and the magnitude of an earthquake.
-
-The documentation for each individual style layer property notes which style functions are enabled for that property.
-
-## 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.
-
-```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),
-]
-```
-
-## Interpolation mode
-
-The effect a key has on the style value is determined by the interpolation mode. There are four interpolation modes that can be used with a source style function: exponential, interval, categorical, and identity. You can also use exponential and interval interpolation modes with a camera style function.
-
-### Linear
-
-`MGLInterpolationModeExponential` interpolates linearly or exponentially between style function stop values. By default, the `MGLStyleFunction` options parameter `MGLStyleFunctionOptionInterpolationBase` equals `1`, which represents linear interpolation and doesn’t need to be included in the options dictionary.
-
-The stops dictionary below, for example, shows colors that continuously shift from yellow to orange to red to blue to white based on the attribute value.
-
-```swift
-let url = URL(string: "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson")!
-let symbolSource = MGLSource(identifier: "source")
-let symbolLayer = MGLSymbolStyleLayer(identifier: "place-city-sm", source: symbolSource)
-
-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 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)
-mapView.style?.insertLayer(layer, below: symbolLayer)
-```
-
-![exponential mode](img/data-driven-styling/exponential.png)
-
-### Exponential
-
-By combining `MGLInterpolationModeExponential` with an `MGLStyleFunctionOptionInterpolationBase` greater than `0` (other than `1`), you can interpolate between values exponentially, create an accelerated ramp effect.
-
-Here’s a visualization from Mapbox Studio (see [Working with Mapbox Studio](working-with-mapbox-studio.html)) comparing interpolation base values of `1.5` and `0.5` based on zoom.
-
-<img src="img/data-driven-styling/exponential-function.png" height=344/>
-<img src="img/data-driven-styling/exponential-function-1.png" height=344/>
-
-The example below increases a layer’s `circleRadius` exponentially based on a map’s zoom level. The `MGLStyleFunctionOptionInterpolationBase` is `1.5`.
-
-```swift
-let stops = [
- 12: MGLStyleValue<NSNumber>(rawValue: 0.5),
- 14: MGLStyleValue(rawValue: 2),
- 18: MGLStyleValue(rawValue: 18),
-]
-
-layer.circleRadius = MGLStyleValue(interpolationMode: .exponential,
- cameraStops: stops,
- options: [.interpolationBase: 1.5])
-```
-
-### Interval
-
-`MGLInterpolationModeInterval` creates a range using the keys from the stops dictionary. The range is from the given key to just less than the next key. The attribute values that fall into that range are then styled using the style value assigned to that key.
-
-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),
-]
-
-layer.circleColor = MGLStyleValue(interpolationMode: .interval,
- sourceStops: stops,
- attributeName: "mag",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .green)])
-```
-
-![interval mode](img/data-driven-styling/interval.png)
-
-### Categorical
-
-At each stop, `MGLInterpolationModeCategorical` produces an output value equal to the function input. We’re going to use a different stops dictionary than we did for the previous two modes.
-
-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),
-]
-
-layer.circleColor = MGLStyleValue(interpolationMode: .categorical,
- sourceStops: categoricalStops,
- attributeName: "type",
- options: [.defaultValue: MGLStyleValue<NSColor>(rawValue: .blue)])
-```
-
-![categorical mode](img/data-driven-styling/categorical1.png) ![categorical mode](img/data-driven-styling/categorical2.png)
-
-### Identity
-
-`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)])
-```
-
-![identity mode](img/data-driven-styling/identity.png)
-
-##Resources
-
-* [USGS](https://earthquake.usgs.gov/earthquakes/feed/v1.0/geojson.php)
-* [For Style Authors](for-style-authors.html)
diff --git a/platform/macos/docs/img/screenshot.jpg b/platform/macos/docs/img/screenshot.jpg
index a8c64a69fe..4af752b299 100644
--- a/platform/macos/docs/img/screenshot.jpg
+++ b/platform/macos/docs/img/screenshot.jpg
Binary files differ
diff --git a/platform/macos/docs/pod-README.md b/platform/macos/docs/pod-README.md
index 97e77673ee..4827124be0 100644
--- a/platform/macos/docs/pod-README.md
+++ b/platform/macos/docs/pod-README.md
@@ -10,7 +10,7 @@ Put interactive, scalable world maps into your native Cocoa application with the
![](https://raw.githubusercontent.com/mapbox/mapbox-gl-native/master/platform/macos/docs/img/screenshot.jpg)
-The Mapbox Maps SDK for macOS is compatible with macOS 10.10.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/).
+The Mapbox Maps SDK for macOS is compatible with macOS 10.11.0 and above for Cocoa applications developed in Objective-C, Swift, Interface Builder, or AppleScript. For hybrid applications, consider [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/).
## Installation
@@ -37,7 +37,7 @@ After running `carthage update`, you’ll find Mapbox.framework in the Carthage/
Create a [Podfile](https://guides.cocoapods.org/syntax/podfile.html) with the following specification:
```rb
-platform :osx, '10.10'
+platform :osx, '10.11'
target 'TargetNameForYourApp' do
pod 'Mapbox-iOS-SDK', '~> x.y'
diff --git a/platform/macos/jazzy.yml b/platform/macos/jazzy.yml
index ab4643fa65..e1053324fb 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,8 +18,9 @@ custom_categories:
- name: Guides
children:
- Working with GeoJSON Data
+ - Predicates and Expressions
- For Style Authors
- - Using Style Functions at Runtime
+ - Style Layers Using Expressions
- Tile URL Templates
- Info.plist Keys
- name: Maps
@@ -48,7 +49,6 @@ custom_categories:
- name: Styling the Map
children:
- MGLStyle
- - MGLStyleValue
- MGLLight
- name: Content Primitives
children:
@@ -63,10 +63,13 @@ custom_categories:
- name: Content Sources
children:
- MGLSource
- - MGLTileSource
- MGLShapeSource
- - MGLRasterSource
- - MGLVectorSource
+ - MGLComputedShapeSource
+ - MGLTileSource
+ - MGLRasterTileSource
+ - MGLRasterDEMSource
+ - MGLVectorTileSource
+ - MGLImageSource
- name: Style Layers
children:
- MGLStyleLayer
@@ -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..002590d8ff 100644
--- a/platform/macos/macos.xcodeproj/project.pbxproj
+++ b/platform/macos/macos.xcodeproj/project.pbxproj
@@ -9,7 +9,13 @@
/* 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 */; };
+ 07D9474D1F67441B00E37934 /* MGLComputedShapeSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 07D947471F6741F500E37934 /* MGLComputedShapeSource_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,17 +23,15 @@
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, ); }; };
352742811D4C243B00A1ECE6 /* MGLSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 3527427F1D4C243B00A1ECE6 /* MGLSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
352742821D4C243B00A1ECE6 /* MGLSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352742801D4C243B00A1ECE6 /* MGLSource.mm */; };
- 352742851D4C244700A1ECE6 /* MGLRasterSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742831D4C244700A1ECE6 /* MGLRasterSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 352742861D4C244700A1ECE6 /* MGLRasterSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352742841D4C244700A1ECE6 /* MGLRasterSource.mm */; };
+ 352742851D4C244700A1ECE6 /* MGLRasterTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742831D4C244700A1ECE6 /* MGLRasterTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 352742861D4C244700A1ECE6 /* MGLRasterTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352742841D4C244700A1ECE6 /* MGLRasterTileSource.mm */; };
352742891D4C245800A1ECE6 /* MGLShapeSource.h in Headers */ = {isa = PBXBuildFile; fileRef = 352742871D4C245800A1ECE6 /* MGLShapeSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
3527428A1D4C245800A1ECE6 /* MGLShapeSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = 352742881D4C245800A1ECE6 /* MGLShapeSource.mm */; };
3527428D1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -49,7 +53,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 +69,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 +85,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, ); }; };
@@ -118,8 +121,8 @@
DA6408D81DA4E5DA00908C90 /* MGLVectorStyleLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = DA6408D61DA4E5DA00908C90 /* MGLVectorStyleLayer.m */; };
DA7262071DEEDD460043BB89 /* MGLOpenGLStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7262051DEEDD460043BB89 /* MGLOpenGLStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA7262081DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA7262061DEEDD460043BB89 /* MGLOpenGLStyleLayer.mm */; };
- DA7DC9811DED5F5C0027472F /* MGLVectorSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */; };
- DA7DC9831DED647F0027472F /* MGLRasterSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7DC9821DED647F0027472F /* MGLRasterSource_Private.h */; };
+ DA7DC9811DED5F5C0027472F /* MGLVectorTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7DC9801DED5F5C0027472F /* MGLVectorTileSource_Private.h */; };
+ DA7DC9831DED647F0027472F /* MGLRasterTileSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA7DC9821DED647F0027472F /* MGLRasterTileSource_Private.h */; };
DA839E971CC2E3400062CAFB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E961CC2E3400062CAFB /* AppDelegate.m */; };
DA839E9A1CC2E3400062CAFB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E991CC2E3400062CAFB /* main.m */; };
DA839E9D1CC2E3400062CAFB /* MapDocument.m in Sources */ = {isa = PBXBuildFile; fileRef = DA839E9C1CC2E3400062CAFB /* MapDocument.m */; };
@@ -149,8 +152,8 @@
DA8F25901D51CA600010E6B5 /* MGLRasterStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F258E1D51CA600010E6B5 /* MGLRasterStyleLayer.mm */; };
DA8F25931D51CA750010E6B5 /* MGLSymbolStyleLayer.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25911D51CA750010E6B5 /* MGLSymbolStyleLayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
DA8F25941D51CA750010E6B5 /* MGLSymbolStyleLayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25921D51CA750010E6B5 /* MGLSymbolStyleLayer.mm */; };
- DA8F25971D51CAC70010E6B5 /* MGLVectorSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
- DA8F25981D51CAC70010E6B5 /* MGLVectorSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */; };
+ DA8F25971D51CAC70010E6B5 /* MGLVectorTileSource.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25951D51CAC70010E6B5 /* MGLVectorTileSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ DA8F25981D51CAC70010E6B5 /* MGLVectorTileSource.mm in Sources */ = {isa = PBXBuildFile; fileRef = DA8F25961D51CAC70010E6B5 /* MGLVectorTileSource.mm */; };
DA8F259A1D51CAD00010E6B5 /* MGLSource_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25991D51CAD00010E6B5 /* MGLSource_Private.h */; };
DA8F259C1D51CB000010E6B5 /* MGLStyleValue_Private.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F259B1D51CB000010E6B5 /* MGLStyleValue_Private.h */; };
DA8F25B21D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */; };
@@ -160,7 +163,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 +252,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 +294,13 @@
/* 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 /* MGLComputedShapeSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLComputedShapeSource_Private.h; 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 +308,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>"; };
@@ -302,8 +315,8 @@
352742771D4C220900A1ECE6 /* MGLStyleValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValue.h; sourceTree = "<group>"; };
3527427F1D4C243B00A1ECE6 /* MGLSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSource.h; sourceTree = "<group>"; };
352742801D4C243B00A1ECE6 /* MGLSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLSource.mm; sourceTree = "<group>"; };
- 352742831D4C244700A1ECE6 /* MGLRasterSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource.h; sourceTree = "<group>"; };
- 352742841D4C244700A1ECE6 /* MGLRasterSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterSource.mm; sourceTree = "<group>"; };
+ 352742831D4C244700A1ECE6 /* MGLRasterTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterTileSource.h; sourceTree = "<group>"; };
+ 352742841D4C244700A1ECE6 /* MGLRasterTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterTileSource.mm; sourceTree = "<group>"; };
352742871D4C245800A1ECE6 /* MGLShapeSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeSource.h; sourceTree = "<group>"; };
352742881D4C245800A1ECE6 /* MGLShapeSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeSource.mm; sourceTree = "<group>"; };
3527428B1D4C24AB00A1ECE6 /* MGLCircleStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLCircleStyleLayer.h; sourceTree = "<group>"; };
@@ -311,7 +324,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 +337,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 +356,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 +372,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 +400,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>"; };
@@ -439,8 +450,8 @@
DA737AE31E5915A500AD2CDE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = pl; path = pl.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA737AE41E5915B000AD2CDE /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
DA737AEC1E59180E00AD2CDE /* uk */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
- DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource_Private.h; sourceTree = "<group>"; };
- DA7DC9821DED647F0027472F /* MGLRasterSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterSource_Private.h; sourceTree = "<group>"; };
+ DA7DC9801DED5F5C0027472F /* MGLVectorTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorTileSource_Private.h; sourceTree = "<group>"; };
+ DA7DC9821DED647F0027472F /* MGLRasterTileSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLRasterTileSource_Private.h; sourceTree = "<group>"; };
DA80E95D1FE84A300065FC9B /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
DA80E95F1FE84A540065FC9B /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ar; path = ar.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DA839E921CC2E3400062CAFB /* Mapbox GL.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Mapbox GL.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -477,8 +488,8 @@
DA8F258E1D51CA600010E6B5 /* MGLRasterStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLRasterStyleLayer.mm; sourceTree = "<group>"; };
DA8F25911D51CA750010E6B5 /* MGLSymbolStyleLayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSymbolStyleLayer.h; sourceTree = "<group>"; };
DA8F25921D51CA750010E6B5 /* MGLSymbolStyleLayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLSymbolStyleLayer.mm; sourceTree = "<group>"; };
- DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorSource.h; sourceTree = "<group>"; };
- DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorSource.mm; sourceTree = "<group>"; };
+ DA8F25951D51CAC70010E6B5 /* MGLVectorTileSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLVectorTileSource.h; sourceTree = "<group>"; };
+ DA8F25961D51CAC70010E6B5 /* MGLVectorTileSource.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLVectorTileSource.mm; sourceTree = "<group>"; };
DA8F25991D51CAD00010E6B5 /* MGLSource_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLSource_Private.h; sourceTree = "<group>"; };
DA8F259B1D51CB000010E6B5 /* MGLStyleValue_Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLStyleValue_Private.h; sourceTree = "<group>"; };
DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSValue+MGLStyleAttributeAdditions.h"; sourceTree = "<group>"; };
@@ -486,6 +497,10 @@
DA8F25B51D51D2240010E6B5 /* MGLStyleLayerTests.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; name = MGLStyleLayerTests.mm.ejs; path = ../test/MGLStyleLayerTests.mm.ejs; sourceTree = "<group>"; };
DA8F25B61D51D2240010E6B5 /* MGLStyleLayer.h.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.h.ejs; sourceTree = "<group>"; };
DA8F25B71D51D2240010E6B5 /* MGLStyleLayer.mm.ejs */ = {isa = PBXFileReference; lastKnownFileType = text; path = MGLStyleLayer.mm.ejs; sourceTree = "<group>"; };
+ DA934097208562590059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DA934098208562870059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-PT"; path = "pt-PT.lproj/Foundation.stringsdict"; sourceTree = "<group>"; };
+ DA9340992085629E0059919A /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
+ DA9340A0208565870059919A /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = "<group>"; };
DA9C01301E4C7B9300C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = "pt-BR"; path = "pt-BR.lproj/Foundation.stringsdict"; sourceTree = "<group>"; };
DA9C01311E4C7B9F00C4742A /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
DAA32CA61E4C4849006F8D24 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.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>"; };
@@ -515,6 +536,10 @@
DACFE7971F66EA0C00630DA8 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = vi; path = vi.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
DAD165721CF4CD7A001FF4B9 /* MGLShapeCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MGLShapeCollection.h; sourceTree = "<group>"; };
DAD165731CF4CD7A001FF4B9 /* MGLShapeCollection.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MGLShapeCollection.mm; sourceTree = "<group>"; };
+ DAD88E03202ACF5900AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
+ DAD88E04202ACF7C00AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Foundation.strings; sourceTree = "<group>"; };
+ DAD88E05202ACF8200AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = da; path = da.lproj/Foundation.stringsdict; sourceTree = "<group>"; };
+ DAD88E06202ACF9200AAA536 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
DAE6C2E11CC304F900DB3429 /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
DAE6C2E31CC3050F00DB3429 /* DroppedPinAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DroppedPinAnnotation.h; sourceTree = "<group>"; };
DAE6C2E41CC3050F00DB3429 /* DroppedPinAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DroppedPinAnnotation.m; sourceTree = "<group>"; };
@@ -605,6 +630,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 +686,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 +710,6 @@
352742791D4C235C00A1ECE6 /* Categories */ = {
isa = PBXGroup;
children = (
- 1FCDF1401F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.h */,
- 1FCDF1411F2A4F3600A46694 /* MGLVectorSource+MGLAdditions.m */,
DA8F25A61D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.h */,
DA8F25A71D51CB270010E6B5 /* NSValue+MGLStyleAttributeAdditions.mm */,
);
@@ -689,9 +719,14 @@
3527427E1D4C242B00A1ECE6 /* Sources */ = {
isa = PBXGroup;
children = (
- 352742831D4C244700A1ECE6 /* MGLRasterSource.h */,
- DA7DC9821DED647F0027472F /* MGLRasterSource_Private.h */,
- 352742841D4C244700A1ECE6 /* MGLRasterSource.mm */,
+ 07F8E2F41F674C8000F794BB /* MGLComputedShapeSource.h */,
+ 07D947471F6741F500E37934 /* MGLComputedShapeSource_Private.h */,
+ 07F8E2F51F674C8000F794BB /* MGLComputedShapeSource.mm */,
+ 352742831D4C244700A1ECE6 /* MGLRasterTileSource.h */,
+ DA7DC9821DED647F0027472F /* MGLRasterTileSource_Private.h */,
+ 352742841D4C244700A1ECE6 /* MGLRasterTileSource.mm */,
+ DACA8620201920BE00E9693A /* MGLRasterDEMSource.h */,
+ DACA8621201920BE00E9693A /* MGLRasterDEMSource.mm */,
352742871D4C245800A1ECE6 /* MGLShapeSource.h */,
DA87A99B1DC9D8DD00810D09 /* MGLShapeSource_Private.h */,
352742881D4C245800A1ECE6 /* MGLShapeSource.mm */,
@@ -701,9 +736,9 @@
DA551B7F1DB496AC0009AFAF /* MGLTileSource.h */,
DA551B801DB496AC0009AFAF /* MGLTileSource_Private.h */,
DA551B811DB496AC0009AFAF /* MGLTileSource.mm */,
- DA8F25951D51CAC70010E6B5 /* MGLVectorSource.h */,
- DA7DC9801DED5F5C0027472F /* MGLVectorSource_Private.h */,
- DA8F25961D51CAC70010E6B5 /* MGLVectorSource.mm */,
+ DA8F25951D51CAC70010E6B5 /* MGLVectorTileSource.h */,
+ DA7DC9801DED5F5C0027472F /* MGLVectorTileSource_Private.h */,
+ DA8F25961D51CAC70010E6B5 /* MGLVectorTileSource.mm */,
07A019EB1ED662D800ACD43E /* MGLImageSource.h */,
07A019EC1ED662D800ACD43E /* MGLImageSource.mm */,
);
@@ -804,6 +839,7 @@
DA839EA61CC2E3400062CAFB /* Info.plist */,
96E027331E57C9A7004B8E66 /* Localizable.strings */,
DA839E981CC2E3400062CAFB /* Supporting Files */,
+ 89462398200D199100DA8EF2 /* heatmap.json */,
);
name = "Demo App";
path = app;
@@ -820,6 +856,7 @@
DA87A99A1DC9D88800810D09 /* Sources */ = {
isa = PBXGroup;
children = (
+ 07A240921F67566F002C8210 /* MGLComputedShapeSourceTests.m */,
DA87A9961DC9D88400810D09 /* MGLShapeSourceTests.mm */,
DA87A9971DC9D88400810D09 /* MGLTileSetTests.mm */,
920A3E581E6F859D00C16EFC /* MGLSourceQueryTests.m */,
@@ -853,11 +890,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 +913,6 @@
DA8F257C1D51C5F40010E6B5 /* Layers */,
DA87A99A1DC9D88800810D09 /* Sources */,
1F7454AA1ED1DDBD00021D39 /* MGLLightTest.mm */,
- 353722EB1DF850ED004D2F3F /* MGLStyleValueTests.h */,
- 3599A3E71DF70E2000E77FB2 /* MGLStyleValueTests.m */,
- DA2207BB1DC076940002F84D /* MGLStyleValueTests.swift */,
);
name = Styling;
path = ../../darwin/test;
@@ -938,8 +975,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 +1009,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 +1025,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 +1181,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 */,
+ DA7DC9831DED647F0027472F /* MGLRasterTileSource_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 */,
@@ -1177,17 +1214,20 @@
DAE6C35F1CC31E0400DB3429 /* MGLOfflinePack.h in Headers */,
DAE6C39C1CC31E2A00DB3429 /* NSString+MGLAdditions.h in Headers */,
3529039B1D6C63B80002C7DF /* NSPredicate+MGLAdditions.h in Headers */,
- DA8F25971D51CAC70010E6B5 /* MGLVectorSource.h in Headers */,
- DA7DC9811DED5F5C0027472F /* MGLVectorSource_Private.h in Headers */,
+ DA8F25971D51CAC70010E6B5 /* MGLVectorTileSource.h in Headers */,
+ DA7DC9811DED5F5C0027472F /* MGLVectorTileSource_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 /* MGLComputedShapeSource_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 */,
@@ -1207,12 +1247,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 */,
@@ -1235,9 +1274,11 @@
DAE6C3891CC31E2A00DB3429 /* MGLMultiPoint_Private.h in Headers */,
DAE6C3A51CC31E9400DB3429 /* MGLMapView+IBAdditions.h in Headers */,
DA35A2AD1CCA091800E826B2 /* MGLCompassDirectionFormatter.h in Headers */,
- 352742851D4C244700A1ECE6 /* MGLRasterSource.h in Headers */,
+ 352742851D4C244700A1ECE6 /* MGLRasterTileSource.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,12 +1370,12 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = MBX;
- LastUpgradeCheck = 0800;
+ LastUpgradeCheck = 0920;
ORGANIZATIONNAME = Mapbox;
TargetAttributes = {
DA839E911CC2E3400062CAFB = {
CreatedOnToolsVersion = 7.3;
- LastSwiftMigration = 0830;
+ LastSwiftMigration = 0920;
};
DAAA17961CE13BAE00731EFE = {
CreatedOnToolsVersion = 7.3.1;
@@ -1344,7 +1385,7 @@
};
DAE6C3301CC30DB200DB3429 = {
CreatedOnToolsVersion = 7.3;
- LastSwiftMigration = 0800;
+ LastSwiftMigration = 0920;
};
};
};
@@ -1374,6 +1415,9 @@
hu,
bg,
ar,
+ he,
+ da,
+ "pt-PT",
);
mainGroup = DA839E891CC2E3400062CAFB;
productRefGroup = DA839E931CC2E3400062CAFB /* Products */;
@@ -1397,6 +1441,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 +1499,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 */,
@@ -1471,12 +1516,13 @@
DAE6C3B31CC31EF300DB3429 /* MGLAttributionButton.mm in Sources */,
35602BFB1D3EA99F0050646F /* MGLFillStyleLayer.mm in Sources */,
DAE6C3931CC31E2A00DB3429 /* MGLShape.mm in Sources */,
- 352742861D4C244700A1ECE6 /* MGLRasterSource.mm in Sources */,
+ 352742861D4C244700A1ECE6 /* MGLRasterTileSource.mm in Sources */,
558DE7A71E56161C00C7916D /* MGLFoundation.mm in Sources */,
DAE6C39D1CC31E2A00DB3429 /* NSString+MGLAdditions.m in Sources */,
3598195A1E02F611008FC139 /* NSCoder+MGLAdditions.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 +1532,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 +1549,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,11 +1559,11 @@
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 */,
+ DA8F25981D51CAC70010E6B5 /* MGLVectorTileSource.mm in Sources */,
352742A11D4C25BD00A1ECE6 /* MGLStyleValue.mm in Sources */,
3EA93BA38DBB4B814B6C1FCC /* MGLRendererConfiguration.mm in Sources */,
);
@@ -1531,6 +1577,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 +1585,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 +1608,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 +1654,9 @@
DA704CBE1F637531004B3F28 /* hu */,
DA3389611FA3EDCE001EA329 /* bg */,
DA80E95D1FE84A300065FC9B /* ar */,
+ DACBC6082011885800C4D7E2 /* he */,
+ DAD88E03202ACF5900AAA536 /* da */,
+ DA934097208562590059919A /* pt-PT */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1656,6 +1707,10 @@
DAE8CCAC1E6E8B8D009B5CB0 /* nl */,
DA704CBF1F637548004B3F28 /* hu */,
DA3389641FA3EE00001EA329 /* bg */,
+ DACBC60A2011889E00C4D7E2 /* he */,
+ DAD88E06202ACF9200AAA536 /* da */,
+ DA9340992085629E0059919A /* pt-PT */,
+ DA9340A0208565870059919A /* ar */,
);
name = Localizable.strings;
sourceTree = "<group>";
@@ -1678,6 +1733,8 @@
DA704CC61F666385004B3F28 /* uk */,
DA3389601FA3EAC4001EA329 /* pt-BR */,
DA3389621FA3EDEF001EA329 /* bg */,
+ DACBC6092011888C00C4D7E2 /* he */,
+ DAD88E04202ACF7C00AAA536 /* da */,
);
name = Foundation.strings;
sourceTree = "<group>";
@@ -1700,6 +1757,8 @@
DA3389631FA3EDF5001EA329 /* bg */,
DA33896C1FA3EF51001EA329 /* hu */,
DA80E95F1FE84A540065FC9B /* ar */,
+ DAD88E05202ACF8200AAA536 /* da */,
+ DA934098208562870059919A /* pt-PT */,
);
name = Foundation.stringsdict;
sourceTree = "<group>";
@@ -1717,7 +1776,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 +1786,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;
@@ -1748,7 +1816,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SYMROOT = "$(PROJECT_DIR)/cmake";
@@ -1765,7 +1833,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 +1843,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;
@@ -1790,8 +1867,9 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- MACOSX_DEPLOYMENT_TARGET = 10.10;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
MTL_ENABLE_DEBUG_INFO = NO;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SYMROOT = "$(PROJECT_DIR)/cmake";
};
name = Release;
@@ -1808,7 +1886,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = "Mapbox GL";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -1823,7 +1901,7 @@
OTHER_CFLAGS = "-fvisibility=hidden";
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.MapboxGL;
PRODUCT_NAME = "Mapbox GL";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 4.0;
};
name = Release;
};
@@ -1949,7 +2027,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "../darwin/test/test-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 4.0;
};
name = Debug;
};
@@ -1976,7 +2054,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.mapbox.test;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "../darwin/test/test-Bridging-Header.h";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 4.0;
};
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..aab7486fbe 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 = "en"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
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/ar.lproj/Localizable.strings b/platform/macos/sdk/ar.lproj/Localizable.strings
new file mode 100644
index 0000000000..fb0b0a2a46
--- /dev/null
+++ b/platform/macos/sdk/ar.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" = "قرّب";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "بعّد";
+
diff --git a/platform/macos/sdk/da.lproj/Localizable.strings b/platform/macos/sdk/da.lproj/Localizable.strings
new file mode 100644
index 0000000000..1c9dcaa68d
--- /dev/null
+++ b/platform/macos/sdk/da.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi der opstod en ukendt fejl.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen ikke kan indlæses.";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen er beskadiget.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Kortet kunne ikke indlæses, fordi benævnelsen ikke kan findes eller er uforenelig.";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Zoom Ind";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Zoom Ud";
+
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/hu.lproj/Localizable.strings b/platform/macos/sdk/hu.lproj/Localizable.strings
index b3724190cb..062b1f5cea 100644
--- a/platform/macos/sdk/hu.lproj/Localizable.strings
+++ b/platform/macos/sdk/hu.lproj/Localizable.strings
@@ -1,8 +1,8 @@
-/* User-friendly error description */
+/* User-friendly error description */
"LOAD_MAP_FAILED_DESC" = "Egy ismeretlen hiba miatt nem sikerült betölteni a térképet.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílus nem tölthető be.";
+"LOAD_STYLE_FAILED_DESC" = "Nem sikerült betölteni a térképet, mert a stílust nem lehetett betölteni.";
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
diff --git a/platform/macos/sdk/pt-PT.lproj/Localizable.strings b/platform/macos/sdk/pt-PT.lproj/Localizable.strings
new file mode 100644
index 0000000000..1385193989
--- /dev/null
+++ b/platform/macos/sdk/pt-PT.lproj/Localizable.strings
@@ -0,0 +1,27 @@
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Não foi possível carregar o mapa porque ocorreu um erro.";
+
+/* User-friendly error description */
+"LOAD_STYLE_FAILED_DESC" = "Não foi possível carregar o mapa porque o estilo não foi carregado.";
+
+/* Accessibility title */
+"MAP_A11Y_TITLE" = "Mapbox";
+
+/* User-friendly error description */
+"PARSE_STYLE_FAILED_DESC" = "Não foi possível carregar o mapa porque o estilo está corrompido.";
+
+/* User-friendly error description */
+"STYLE_NOT_FOUND_DESC" = "Não foi possível carregar o mapa porque o estilo não foi encontrado ou não é compatível.";
+
+/* Label of Zoom In button */
+"ZOOM_IN_LABEL" = "+";
+
+/* Tooltip of Zoom In button */
+"ZOOM_IN_TOOLTIP" = "Apromixar";
+
+/* Label of Zoom Out button; U+2212 MINUS SIGN */
+"ZOOM_OUT_LABEL" = "−";
+
+/* Tooltip of Zoom Out button */
+"ZOOM_OUT_TOOLTIP" = "Afastar";
+
diff --git a/platform/macos/sdk/ru.lproj/Localizable.strings b/platform/macos/sdk/ru.lproj/Localizable.strings
index 067e05b14e..8554c9ebd1 100644
--- a/platform/macos/sdk/ru.lproj/Localizable.strings
+++ b/platform/macos/sdk/ru.lproj/Localizable.strings
@@ -1,8 +1,8 @@
-/* User-friendly error description */
-"LOAD_MAP_FAILED_DESC" = "Не удалось загрузит карту из-за неизвестной ошибки.";
+/* User-friendly error description */
+"LOAD_MAP_FAILED_DESC" = "Не удалось загрузить карту из-за неизвестной ошибки.";
/* User-friendly error description */
-"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту так как невозможно загрузить стиль.";
+"LOAD_STYLE_FAILED_DESC" = "Не удалось загрузить карту из-за ошибки загрузки стиля.";
/* Accessibility title */
"MAP_A11Y_TITLE" = "Mapbox";
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+IBAdditions.mm b/platform/macos/src/MGLMapView+IBAdditions.mm
index eada47ef90..cef2863ee6 100644
--- a/platform/macos/src/MGLMapView+IBAdditions.mm
+++ b/platform/macos/src/MGLMapView+IBAdditions.mm
@@ -4,7 +4,7 @@
@implementation MGLMapView (IBAdditions)
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyleURL__ {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyleURL__ {
return [NSSet setWithObject:@"styleURL"];
}
@@ -23,7 +23,7 @@
self.styleURL = url;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLatitude {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingLatitude {
return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil];
}
@@ -45,7 +45,7 @@
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingLongitude {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingLongitude {
return [NSSet setWithObjects:@"centerCoordinate", @"camera", nil];
}
@@ -67,7 +67,7 @@
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsZooming {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsZooming {
return [NSSet setWithObject:@"zoomEnabled"];
}
@@ -79,7 +79,7 @@
self.zoomEnabled = allowsZooming;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsScrolling {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsScrolling {
return [NSSet setWithObject:@"scrollEnabled"];
}
@@ -91,7 +91,7 @@
self.scrollEnabled = allowsScrolling;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsRotating {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsRotating {
return [NSSet setWithObject:@"rotateEnabled"];
}
@@ -103,7 +103,7 @@
self.rotateEnabled = allowsRotating;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsTilting {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingAllowsTilting {
return [NSSet setWithObject:@"pitchEnabled"];
}
diff --git a/platform/macos/src/MGLMapView.h b/platform/macos/src/MGLMapView.h
index 96b0932c14..824df827ac 100644
--- a/platform/macos/src/MGLMapView.h
+++ b/platform/macos/src/MGLMapView.h
@@ -397,11 +397,10 @@ MGL_EXPORT IB_DESIGNABLE
want to animate the change, use the `-setVisibleCoordinateBounds:animated:`
method instead.
- If a longitude is less than −180 degrees or greater than 180 degrees, the visible
- bounds straddles the antimeridian or international date line.
-
- For example, a visible bounds that stretches from Tokyo to San Francisco would have
- coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+ If a longitude is less than −180 degrees or greater than 180 degrees, the
+ visible bounds straddles the antimeridian or international date line. For
+ example, if both Tokyo and San Francisco are visible, the visible bounds might
+ extend from (35.68476, −220.24257) to (37.78428, −122.41310).
*/
@property (nonatomic) MGLCoordinateBounds visibleCoordinateBounds;
@@ -409,11 +408,10 @@ MGL_EXPORT IB_DESIGNABLE
Changes the receiver’s viewport to fit the given coordinate bounds, optionally
animating the change.
- To make the visible bounds go across the antimeridian or international date line,
- specify some longitudes less than −180 degrees or greater than 180 degrees.
-
- For example, a visible bounds that stretches from Tokyo to San Francisco would have
- coordinates of (35.68476, -220.24257) and (37.78428, -122.41310).
+ To bring both sides of the antimeridian or international date line into view,
+ specify some longitudes less than −180 degrees or greater than 180 degrees. For
+ example, to show both Tokyo and San Francisco simultaneously, you could set the
+ visible bounds to extend from (35.68476, −220.24257) to (37.78428, −122.41310).
@param bounds The bounds that the viewport will show in its entirety.
@param animated Specify `YES` to animate the change by smoothly scrolling and
@@ -445,7 +443,7 @@ MGL_EXPORT IB_DESIGNABLE
@param animated `YES` if you want the map region change to be animated, or `NO`
if you want the map to display the new region immediately without animations.
*/
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated;
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated;
/**
Sets the visible region so that the map displays the specified annotations with
@@ -460,7 +458,7 @@ MGL_EXPORT IB_DESIGNABLE
@param animated `YES` if you want the map region change to be animated, or `NO`
if you want the map to display the new region immediately without animations.
*/
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated;
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated;
/**
Returns the camera that best fits the given coordinate bounds.
@@ -623,7 +621,7 @@ MGL_EXPORT IB_DESIGNABLE
annotations are associated with the map view, the value of this property is
`nil`.
*/
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *annotations;
+@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *annotations;
/**
Adds an annotation to the map view.
@@ -653,7 +651,7 @@ MGL_EXPORT IB_DESIGNABLE
must conform to the `MGLAnnotation` protocol. The map view retains each
individual annotation object.
*/
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations;
/**
The complete list of annotations associated with the receiver that are
@@ -663,7 +661,7 @@ MGL_EXPORT IB_DESIGNABLE
annotations are associated with the map view or if no annotations associated
with the map view are currently visible, the value of this property is `nil`.
*/
-@property (nonatomic, readonly, nullable) NS_ARRAY_OF(id <MGLAnnotation>) *visibleAnnotations;
+@property (nonatomic, readonly, nullable) NSArray<id <MGLAnnotation>> *visibleAnnotations;
/**
Removes an annotation from the map view, deselecting it if it is selected.
@@ -688,7 +686,7 @@ MGL_EXPORT IB_DESIGNABLE
@param annotations The array of annotation objects to remove. Objects in the
array must conform to the `MGLAnnotation` protocol.
*/
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations;
+- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations;
/**
Returns a reusable annotation image object associated with its identifier.
@@ -714,7 +712,7 @@ MGL_EXPORT IB_DESIGNABLE
no annotations associated with the map view are currently visible in the
rectangle.
*/
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect;
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect;
#pragma mark Managing Annotation Selections
@@ -723,16 +721,27 @@ MGL_EXPORT IB_DESIGNABLE
Assigning a new array to this property selects only the first annotation in the
array.
+
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the map is
+ panned so that the annotation and its callout are brought just onscreen. The
+ annotation is *not* centered within the viewport.
+
+ @note In versions prior to `4.0.0` if the annotation was offscreen it was not
+ selected.
*/
-@property (nonatomic, copy) NS_ARRAY_OF(id <MGLAnnotation>) *selectedAnnotations;
+@property (nonatomic, copy) NSArray<id <MGLAnnotation>> *selectedAnnotations;
/**
Selects an annotation and displays a callout popover for it.
- If the given annotation is not visible within the current viewport, this method
- has no effect.
+ If the annotation is of type `MGLPointAnnotation` and is offscreen, the map is
+ panned so that the annotation and its callout are brought just onscreen. The
+ annotation is *not* centered within the viewport.
@param annotation The annotation object to select.
+
+ @note In versions prior to `4.0.0` selecting an offscreen annotation did not
+ change the camera.
*/
- (void)selectAnnotation:(id <MGLAnnotation>)annotation;
@@ -782,7 +791,7 @@ MGL_EXPORT IB_DESIGNABLE
overlays are associated with the map view, the value of this property is
empty array.
*/
-@property (nonatomic, readonly, nonnull) NS_ARRAY_OF(id <MGLOverlay>) *overlays;
+@property (nonatomic, readonly, nonnull) NSArray<id <MGLOverlay>> *overlays;
/**
Adds a single overlay to the map.
@@ -802,7 +811,7 @@ MGL_EXPORT IB_DESIGNABLE
@param overlays An array of objects, each of which must conform to the
`MGLOverlay` protocol.
*/
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays;
/**
Removes a single overlay from the map.
@@ -822,7 +831,7 @@ MGL_EXPORT IB_DESIGNABLE
@param overlays An array of objects, each of which conforms to the `MGLOverlay`
protocol.
*/
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays;
+- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays;
#pragma mark Accessing the Underlying Map Data
@@ -838,7 +847,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point NS_SWIFT_NAME(visibleFeatures(at:));
/**
Returns an array of rendered map features that intersect with a given point,
@@ -857,7 +866,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
/**
Returns an array of rendered map features that intersect with a given point,
@@ -866,9 +875,9 @@ MGL_EXPORT IB_DESIGNABLE
Each object in the returned array represents a feature rendered by the
current style and provides access to attributes specified by the relevant map
content sources. The returned array includes features loaded by
- `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
- from `MGLRasterSource` objects, or from image, video, or canvas sources, which
- are unsupported by this SDK.
+ `MGLShapeSource` and `MGLVectorTileSource` objects but does not include
+ anything from `MGLRasterTileSource` objects, or from video or canvas sources,
+ which are unsupported by this SDK.
The returned features are drawn by a style layer in the current style. For
example, suppose the current style uses the
@@ -900,7 +909,7 @@ MGL_EXPORT IB_DESIGNABLE
Only visible features are returned. To obtain features regardless of
visibility, use the
- `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLVectorTileSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
@note Layer identifiers are not guaranteed to exist across styles or different
@@ -919,7 +928,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:predicate:));
/**
Returns an array of rendered map features that intersect with the given
@@ -934,7 +943,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect NS_SWIFT_NAME(visibleFeatures(in:));
/**
Returns an array of rendered map features that intersect with the given
@@ -953,7 +962,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers NS_SWIFT_NAME(visibleFeatures(at:styleLayerIdentifiers:));
/**
Returns an array of rendered map features that intersect with the given
@@ -963,9 +972,9 @@ MGL_EXPORT IB_DESIGNABLE
Each object in the returned array represents a feature rendered by the
current style and provides access to attributes specified by the relevant map
content sources. The returned array includes features loaded by
- `MGLShapeSource` and `MGLVectorSource` objects but does not include anything
- from `MGLRasterSource` objects, or from image, video, or canvas sources, which
- are unsupported by this SDK.
+ `MGLShapeSource` and `MGLVectorTileSource` objects but does not include
+ anything from `MGLRasterTileSource` objects, or from video or canvas sources,
+ which are unsupported by this SDK.
The returned features are drawn by a style layer in the current style. For
example, suppose the current style uses the
@@ -998,7 +1007,7 @@ MGL_EXPORT IB_DESIGNABLE
Only visible features are returned. To obtain features regardless of
visibility, use the
- `-[MGLVectorSource featuresInSourceLayersWithIdentifiers:predicate:]` and
+ `-[MGLVectorTileSource featuresInSourceLayersWithIdentifiers:predicate:]` and
`-[MGLShapeSource featuresMatchingPredicate:]` methods on the relevant sources.
@note Layer identifiers are not guaranteed to exist across styles or different
@@ -1017,7 +1026,7 @@ MGL_EXPORT IB_DESIGNABLE
@return An array of objects conforming to the `MGLFeature` protocol that
represent features in the sources used by the current style.
*/
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:));
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(nullable NSSet<NSString *> *)styleLayerIdentifiers predicate:(nullable NSPredicate *)predicate NS_SWIFT_NAME(visibleFeatures(in:styleLayerIdentifiers:predicate:));
#pragma mark Converting Geographic Coordinates
@@ -1060,6 +1069,9 @@ MGL_EXPORT IB_DESIGNABLE
/**
Converts a rectangle in the given view’s coordinate system to a geographic
bounding box.
+
+ If a longitude is less than −180 degrees or greater than 180 degrees, the
+ bounding box straddles the antimeridian or international date line.
@param rect The rectangle to convert.
@param view The view in whose coordinate system the rectangle is expressed.
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index f2c6afb2f7..42fd24653e 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -97,6 +97,9 @@ const CGFloat MGLAnnotationImagePaddingForHitTest = 4;
/// Distance from the callout’s anchor point to the annotation it points to.
const CGFloat MGLAnnotationImagePaddingForCallout = 4;
+/// Padding to edge of view that an offscreen annotation must have when being brought onscreen (by being selected)
+const NSEdgeInsets MGLMapViewOffscreenAnnotationPadding = NSEdgeInsetsMake(-30.0f, -30.0f, -30.0f, -30.0f);
+
/// Unique identifier representing a single annotation in mbgl.
typedef uint32_t MGLAnnotationTag;
@@ -146,7 +149,7 @@ public:
@property (nonatomic, readwrite) MGLStyle *style;
/// Mapping from reusable identifiers to annotation images.
-@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImagesByIdentifier;
+@property (nonatomic) NSMutableDictionary<NSString *, MGLAnnotationImage *> *annotationImagesByIdentifier;
/// Currently shown popover representing the selected annotation.
@property (nonatomic) NSPopover *calloutForSelectedAnnotation;
@@ -608,7 +611,7 @@ public:
#pragma mark Style
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingStyle {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingStyle {
return [NSSet setWithObject:@"styleURL"];
}
@@ -962,7 +965,7 @@ public:
#pragma mark Viewport
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCenterCoordinate {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCenterCoordinate {
return [NSSet setWithObjects:@"latitude", @"longitude", @"camera", nil];
}
@@ -1012,7 +1015,7 @@ public:
_pendingLongitude = pendingLongitude;
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingZoomLevel {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingZoomLevel {
return [NSSet setWithObject:@"camera"];
}
@@ -1080,7 +1083,7 @@ public:
}
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingDirection {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingDirection {
return [NSSet setWithObject:@"camera"];
}
@@ -1104,7 +1107,7 @@ public:
[self setDirection:_mbglMap->getBearing() + delta animated:animated];
}
-+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCamera {
++ (NSSet<NSString *> *)keyPathsForValuesAffectingCamera {
return [NSSet setWithObjects:@"latitude", @"longitude", @"centerCoordinate", @"zoomLevel", @"direction", nil];
}
@@ -1762,7 +1765,7 @@ public:
#pragma mark Annotations
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)annotations {
+- (nullable NSArray<id <MGLAnnotation>> *)annotations {
if (_annotationContextsByAnnotationTag.empty()) {
return nil;
}
@@ -1778,12 +1781,12 @@ public:
return [NSArray arrayWithObjects:&annotations[0] count:annotations.size()];
}
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotations
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotations
{
return [self visibleFeaturesInRect:self.bounds];
}
-- (nullable NS_ARRAY_OF(id <MGLAnnotation>) *)visibleAnnotationsInRect:(CGRect)rect
+- (nullable NSArray<id <MGLAnnotation>> *)visibleAnnotationsInRect:(CGRect)rect
{
if (_annotationContextsByAnnotationTag.empty())
{
@@ -1849,7 +1852,7 @@ public:
}
}
-- (void)addAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations {
+- (void)addAnnotations:(NSArray<id <MGLAnnotation>> *)annotations {
if (!annotations) {
return;
}
@@ -1984,7 +1987,7 @@ public:
}
}
-- (void)removeAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations {
+- (void)removeAnnotations:(NSArray<id <MGLAnnotation>> *)annotations {
if (!annotations) {
return;
}
@@ -2099,44 +2102,41 @@ public:
MGLAnnotationTag hitAnnotationTag = MGLAnnotationTagNotFound;
if (nearbyAnnotations.size()) {
- // The annotation tags need to be stable in order to compare them with
- // the remembered tags.
- std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end());
-
+ // The first selection in the cycle should be the one nearest to the
+ // tap. Also the annotation tags need to be stable in order to compare them with
+ // the remembered tags _annotationsNearbyLastClick.
+ CLLocationCoordinate2D currentCoordinate = [self convertPoint:point toCoordinateFromView:self];
+ std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
+ CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
+ CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
+ CLLocationDegrees deltaA = hypot(coordinateA.latitude - currentCoordinate.latitude,
+ coordinateA.longitude - currentCoordinate.longitude);
+ CLLocationDegrees deltaB = hypot(coordinateB.latitude - currentCoordinate.latitude,
+ coordinateB.longitude - currentCoordinate.longitude);
+ return deltaA < deltaB;
+ });
+
if (nearbyAnnotations == _annotationsNearbyLastClick) {
- // The first selection in the cycle should be the one nearest to the
- // click.
- CLLocationCoordinate2D currentCoordinate = [self convertPoint:point toCoordinateFromView:self];
- std::sort(nearbyAnnotations.begin(), nearbyAnnotations.end(), [&](const MGLAnnotationTag tagA, const MGLAnnotationTag tagB) {
- CLLocationCoordinate2D coordinateA = [[self annotationWithTag:tagA] coordinate];
- CLLocationCoordinate2D coordinateB = [[self annotationWithTag:tagB] coordinate];
- CLLocationDegrees distanceA = hypot(coordinateA.latitude - currentCoordinate.latitude,
- coordinateA.longitude - currentCoordinate.longitude);
- CLLocationDegrees distanceB = hypot(coordinateB.latitude - currentCoordinate.latitude,
- coordinateB.longitude - currentCoordinate.longitude);
- return distanceA < distanceB;
- });
-
// The last time we persisted a set of annotations, we had the same
// set of annotations as we do now. Cycle through them.
if (_lastSelectedAnnotationTag == MGLAnnotationTagNotFound
- || _lastSelectedAnnotationTag == _annotationsNearbyLastClick.back()) {
+ || _lastSelectedAnnotationTag == nearbyAnnotations.back()) {
// Either no annotation is selected or the last annotation in
// the set was selected. Wrap around to the first annotation in
// the set.
- hitAnnotationTag = _annotationsNearbyLastClick.front();
+ hitAnnotationTag = nearbyAnnotations.front();
} else {
- auto result = std::find(_annotationsNearbyLastClick.begin(),
- _annotationsNearbyLastClick.end(),
+ auto result = std::find(nearbyAnnotations.begin(),
+ nearbyAnnotations.end(),
_lastSelectedAnnotationTag);
- if (result == _annotationsNearbyLastClick.end()) {
+ if (result == nearbyAnnotations.end()) {
// An annotation from this set hasn’t been selected before.
// Select the first (nearest) one.
- hitAnnotationTag = _annotationsNearbyLastClick.front();
+ hitAnnotationTag = nearbyAnnotations.front();
} else {
// Step to the next annotation in the set.
- auto distance = std::distance(_annotationsNearbyLastClick.begin(), result);
- hitAnnotationTag = _annotationsNearbyLastClick[distance + 1];
+ auto distance = std::distance(nearbyAnnotations.begin(), result);
+ hitAnnotationTag = nearbyAnnotations[distance + 1];
}
}
} else {
@@ -2192,12 +2192,12 @@ public:
[self didChangeValueForKey:@"selectedAnnotations"];
}
-- (NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations {
+- (NSArray<id <MGLAnnotation>> *)selectedAnnotations {
id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
return selectedAnnotation ? @[selectedAnnotation] : @[];
}
-- (void)setSelectedAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)selectedAnnotations {
+- (void)setSelectedAnnotations:(NSArray<id <MGLAnnotation>> *)selectedAnnotations {
if (!selectedAnnotations.count) {
return;
}
@@ -2208,10 +2208,12 @@ public:
return;
}
- // Select the annotation if it’s visible.
- if (MGLCoordinateInCoordinateBounds(firstAnnotation.coordinate, self.visibleCoordinateBounds)) {
- [self selectAnnotation:firstAnnotation];
- }
+ [self selectAnnotation:firstAnnotation];
+}
+
+- (BOOL)isBringingAnnotationOnscreenSupportedForAnnotation:(id<MGLAnnotation>)annotation animated:(BOOL)animated {
+ // Consider delegating
+ return animated && [annotation isKindOfClass:[MGLPointAnnotation class]];
}
- (void)selectAnnotation:(id <MGLAnnotation>)annotation
@@ -2221,6 +2223,11 @@ public:
- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint
{
+ [self selectAnnotation:annotation atPoint:gesturePoint moveOnscreen:YES animateSelection:YES];
+}
+
+- (void)selectAnnotation:(id <MGLAnnotation>)annotation atPoint:(NSPoint)gesturePoint moveOnscreen:(BOOL)moveOnscreen animateSelection:(BOOL)animateSelection
+{
id <MGLAnnotation> selectedAnnotation = self.selectedAnnotation;
if (annotation == selectedAnnotation) {
return;
@@ -2235,9 +2242,14 @@ public:
[self addAnnotation:annotation];
}
+ if (moveOnscreen) {
+ moveOnscreen = [self isBringingAnnotationOnscreenSupportedForAnnotation:annotation animated:animateSelection];
+ }
+
// The annotation's anchor will bounce to the current click.
NSRect positioningRect = [self positioningRectForCalloutForAnnotationWithTag:annotationTag];
- if (NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) {
+
+ if (!moveOnscreen && NSIsEmptyRect(NSIntersectionRect(positioningRect, self.bounds))) {
positioningRect = CGRectMake(gesturePoint.x, gesturePoint.y, positioningRect.size.width, positioningRect.size.height);
}
@@ -2257,14 +2269,68 @@ public:
// alignment rect, or off the left edge in a right-to-left UI.
callout.delegate = self;
self.calloutForSelectedAnnotation = callout;
+
NSRectEdge edge = (self.userInterfaceLayoutDirection == NSUserInterfaceLayoutDirectionRightToLeft
? NSMinXEdge
: NSMaxXEdge);
+
+ // The following will do nothing if the positioning rect is not on-screen. See
+ // `-[MGLMapView updateAnnotationCallouts]` for presenting the callout when the selected
+ // annotation comes back on-screen.
[callout showRelativeToRect:positioningRect ofView:self preferredEdge:edge];
}
+
+ if (moveOnscreen)
+ {
+ moveOnscreen = NO;
+
+ NSRect (^edgeInsetsInsetRect)(NSRect, NSEdgeInsets) = ^(NSRect rect, NSEdgeInsets insets) {
+ return NSMakeRect(rect.origin.x + insets.left,
+ rect.origin.y + insets.top,
+ rect.size.width - insets.left - insets.right,
+ rect.size.height - insets.top - insets.bottom);
+ };
+
+ // Add padding around the positioning rect (in essence an inset from the edge of the viewport
+ NSRect expandedPositioningRect = edgeInsetsInsetRect(positioningRect, MGLMapViewOffscreenAnnotationPadding);
+
+ // Used for callout positioning, and moving offscreen annotations onscreen.
+ CGRect constrainedRect = edgeInsetsInsetRect(self.bounds, self.contentInsets);
+ CGRect bounds = constrainedRect;
+
+ // Any one of these cases should trigger a move onscreen
+ if (CGRectGetMinX(positioningRect) < CGRectGetMinX(bounds))
+ {
+ constrainedRect.origin.x = expandedPositioningRect.origin.x;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxX(positioningRect) > CGRectGetMaxX(bounds))
+ {
+ constrainedRect.origin.x = CGRectGetMaxX(expandedPositioningRect) - constrainedRect.size.width;
+ moveOnscreen = YES;
+ }
+
+ if (CGRectGetMinY(positioningRect) < CGRectGetMinY(bounds))
+ {
+ constrainedRect.origin.y = expandedPositioningRect.origin.y;
+ moveOnscreen = YES;
+ }
+ else if (CGRectGetMaxY(positioningRect) > CGRectGetMaxY(bounds))
+ {
+ constrainedRect.origin.y = CGRectGetMaxY(expandedPositioningRect) - constrainedRect.size.height;
+ moveOnscreen = YES;
+ }
+
+ if (moveOnscreen)
+ {
+ CGPoint center = CGPointMake(CGRectGetMidX(constrainedRect), CGRectGetMidY(constrainedRect));
+ CLLocationCoordinate2D centerCoord = [self convertPoint:center toCoordinateFromView:self];
+ [self setCenterCoordinate:centerCoord animated:animateSelection];
+ }
+ }
}
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations animated:(BOOL)animated {
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations animated:(BOOL)animated {
CGFloat maximumPadding = 100;
CGFloat yPadding = (NSHeight(self.bounds) / 5 <= maximumPadding) ? (NSHeight(self.bounds) / 5) : maximumPadding;
CGFloat xPadding = (NSWidth(self.bounds) / 5 <= maximumPadding) ? (NSWidth(self.bounds) / 5) : maximumPadding;
@@ -2274,7 +2340,7 @@ public:
[self showAnnotations:annotations edgePadding:edgeInsets animated:animated];
}
-- (void)showAnnotations:(NS_ARRAY_OF(id <MGLAnnotation>) *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated {
+- (void)showAnnotations:(NSArray<id <MGLAnnotation>> *)annotations edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated {
if ( ! annotations || ! annotations.count) return;
mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
@@ -2396,7 +2462,25 @@ public:
- (void)updateAnnotationCallouts {
NSPopover *callout = self.calloutForSelectedAnnotation;
if (callout) {
- callout.positioningRect = [self positioningRectForCalloutForAnnotationWithTag:_selectedAnnotationTag];
+ NSRect rect = [self positioningRectForCalloutForAnnotationWithTag:_selectedAnnotationTag];
+
+ if (!NSIsEmptyRect(NSIntersectionRect(rect, self.bounds))) {
+
+ // It's possible that the current callout hasn't been presented (since the original
+ // positioningRect was offscreen). We can check that the callout has a valid window
+ // This results in the callout being presented just as the annotation comes on screen
+ // which matches MapKit, but (currently) not iOS.
+ if (!callout.contentViewController.view.window) {
+ NSRectEdge edge = (self.userInterfaceLayoutDirection == NSUserInterfaceLayoutDirectionRightToLeft
+ ? NSMinXEdge
+ : NSMaxXEdge);
+ // Re-present the callout
+ [callout showRelativeToRect:rect ofView:self preferredEdge:edge];
+ }
+ else {
+ callout.positioningRect = rect;
+ }
+ }
}
}
@@ -2453,11 +2537,11 @@ public:
#pragma mark Overlays
-- (nonnull NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+- (nonnull NSArray<id <MGLOverlay>> *)overlays
{
if (self.annotations == nil) { return @[]; }
- NS_MUTABLE_ARRAY_OF(id <MGLOverlay>) *mutableOverlays = [NSMutableArray array];
+ NSMutableArray<id <MGLOverlay>> *mutableOverlays = [NSMutableArray array];
[self.annotations enumerateObjectsUsingBlock:^(id<MGLAnnotation> _Nonnull annotation, NSUInteger idx, BOOL * _Nonnull stop) {
if ([annotation conformsToProtocol:@protocol(MGLOverlay)])
@@ -2473,7 +2557,7 @@ public:
[self addOverlays:@[overlay]];
}
-- (void)addOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays
+- (void)addOverlays:(NSArray<id <MGLOverlay>> *)overlays
{
#if DEBUG
for (id <MGLOverlay> overlay in overlays) {
@@ -2487,7 +2571,7 @@ public:
[self removeOverlays:@[overlay]];
}
-- (void)removeOverlays:(NS_ARRAY_OF(id <MGLOverlay>) *)overlays {
+- (void)removeOverlays:(NSArray<id <MGLOverlay>> *)overlays {
#if DEBUG
for (id <MGLOverlay> overlay in overlays) {
NSAssert([overlay conformsToProtocol:@protocol(MGLOverlay)], @"Overlay does not conform to MGLOverlay");
@@ -2568,15 +2652,15 @@ public:
#pragma mark Data
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point {
return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(CGPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers {
return [self visibleFeaturesAtPoint:point inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesAtPoint:(NSPoint)point inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
// Cocoa origin is at the lower-left corner.
mbgl::ScreenCoordinate screenCoordinate = { point.x, NSHeight(self.bounds) - point.y };
@@ -2599,15 +2683,15 @@ public:
return MGLFeaturesFromMBGLFeatures(features);
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect {
return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(CGRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers {
return [self visibleFeaturesInRect:rect inStyleLayersWithIdentifiers:styleLayerIdentifiers predicate:nil];
}
-- (NS_ARRAY_OF(id <MGLFeature>) *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NS_SET_OF(NSString *) *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
+- (NSArray<id <MGLFeature>> *)visibleFeaturesInRect:(NSRect)rect inStyleLayersWithIdentifiers:(NSSet<NSString *> *)styleLayerIdentifiers predicate:(NSPredicate *)predicate {
// Cocoa origin is at the lower-left corner.
mbgl::ScreenBox screenBox = {
{ NSMinX(rect), NSHeight(self.bounds) - NSMaxY(rect) },
@@ -2717,32 +2801,24 @@ public:
/// Converts a rectangle in the given view’s coordinate system to a geographic
/// bounding box.
- (mbgl::LatLngBounds)convertRect:(NSRect)rect toLatLngBoundsFromView:(nullable NSView *)view {
- mbgl::LatLngBounds bounds = mbgl::LatLngBounds::empty();
- bounds.extend([self convertPoint:rect.origin toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ NSMaxX(rect), NSMinY(rect) } toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ NSMaxX(rect), NSMaxY(rect) } toLatLngFromView:view]);
- bounds.extend([self convertPoint:{ NSMinX(rect), NSMaxY(rect) } toLatLngFromView:view]);
-
- // The world is wrapping if a point just outside the bounds is also within
- // the rect.
- mbgl::LatLng outsideLatLng;
- if (bounds.west() > -180) {
- outsideLatLng = {
- (bounds.south() + bounds.north()) / 2,
- bounds.west() - 1,
- };
- } else if (bounds.northeast().longitude() < 180) {
- outsideLatLng = {
- (bounds.south() + bounds.north()) / 2,
- bounds.east() + 1,
- };
- }
-
- // If the world is wrapping, extend the bounds to cover all longitudes.
- if (NSPointInRect([self convertLatLng:outsideLatLng toPointToView:view], rect)) {
- bounds.extend(mbgl::LatLng(bounds.south(), -180));
- bounds.extend(mbgl::LatLng(bounds.south(), 180));
- }
+ auto bounds = mbgl::LatLngBounds::empty();
+ auto bottomLeft = [self convertPoint:{ NSMinX(rect), NSMinY(rect) } toLatLngFromView:view];
+ auto bottomRight = [self convertPoint:{ NSMaxX(rect), NSMinY(rect) } toLatLngFromView:view];
+ auto topRight = [self convertPoint:{ NSMaxX(rect), NSMaxY(rect) } toLatLngFromView:view];
+ auto topLeft = [self convertPoint:{ NSMinX(rect), NSMaxY(rect) } toLatLngFromView:view];
+
+ // If the bounds straddles the antimeridian, unwrap it so that one side
+ // extends beyond ±180° longitude.
+ auto center = [self convertPoint:{ NSMidX(rect), NSMidY(rect) } toLatLngFromView:view];
+ bottomLeft.unwrapForShortestPath(center);
+ bottomRight.unwrapForShortestPath(center);
+ topRight.unwrapForShortestPath(center);
+ topLeft.unwrapForShortestPath(center);
+
+ bounds.extend(bottomLeft);
+ bounds.extend(bottomRight);
+ bounds.extend(topRight);
+ bounds.extend(topLeft);
return bounds;
}
@@ -2887,7 +2963,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..fcf41203cf 100644
--- a/platform/macos/src/Mapbox.h
+++ b/platform/macos/src/Mapbox.h
@@ -44,12 +44,16 @@ 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 "MGLVectorTileSource.h"
#import "MGLShapeSource.h"
-#import "MGLRasterSource.h"
+#import "MGLComputedShapeSource.h"
+#import "MGLRasterTileSource.h"
+#import "MGLRasterDEMSource.h"
#import "MGLImageSource.h"
#import "MGLTilePyramidOfflineRegion.h"
#import "MGLTypes.h"
@@ -57,3 +61,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/NSColor+MGLAdditions.h b/platform/macos/src/NSColor+MGLAdditions.h
index 8dd8c1c17c..21c939fec6 100644
--- a/platform/macos/src/NSColor+MGLAdditions.h
+++ b/platform/macos/src/NSColor+MGLAdditions.h
@@ -6,7 +6,7 @@
@interface NSColor (MGLAdditions)
/**
- Converts the color into an mbgl::Color in calibrated RGB space.
+ Converts the color into an mbgl::Color in sRGB space.
*/
- (mbgl::Color)mgl_color;
@@ -18,3 +18,10 @@
- (mbgl::style::PropertyValue<mbgl::Color>)mgl_colorPropertyValue;
@end
+
+@interface NSExpression (MGLColorAdditions)
+
++ (NSExpression *)mgl_expressionForRGBComponents:(NSArray<NSExpression *> *)components;
++ (NSExpression *)mgl_expressionForRGBAComponents:(NSArray<NSExpression *> *)components;
+
+@end
diff --git a/platform/macos/src/NSColor+MGLAdditions.mm b/platform/macos/src/NSColor+MGLAdditions.mm
index 5288f2bc61..8c9086ccf7 100644
--- a/platform/macos/src/NSColor+MGLAdditions.mm
+++ b/platform/macos/src/NSColor+MGLAdditions.mm
@@ -2,24 +2,98 @@
@implementation NSColor (MGLAdditions)
-- (mbgl::Color)mgl_color
-{
+- (mbgl::Color)mgl_color {
CGFloat r, g, b, a;
- [[self colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&r green:&g blue:&b alpha:&a];
+ // The Mapbox Style Specification does not specify a color space, but it is
+ // assumed to be sRGB for consistency with CSS.
+ NSColor *srgbColor = self;
+ if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) {
+ srgbColor = [srgbColor colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+ } else {
+ srgbColor = [srgbColor colorUsingColorSpace:[NSColorSpace sRGBColorSpace]];
+ }
+ [srgbColor getRed:&r green:&g blue:&b alpha:&a];
return { (float)r, (float)g, (float)b, (float)a };
}
-+ (NSColor *)mgl_colorWithColor:(mbgl::Color)color
-{
- return [NSColor colorWithCalibratedRed:color.r green:color.g blue:color.b alpha:color.a];
++ (NSColor *)mgl_colorWithColor:(mbgl::Color)color {
+ // macOS 10.12 Sierra and below uses calibrated RGB by default.
+ if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) {
+ return [NSColor colorWithCalibratedRed:color.r green:color.g blue:color.b alpha:color.a];
+ } else {
+ return [NSColor colorWithRed:color.r green:color.g blue:color.b alpha:color.a];
+ }
}
-- (mbgl::style::PropertyValue<mbgl::Color>)mgl_colorPropertyValue
-{
+- (mbgl::style::PropertyValue<mbgl::Color>)mgl_colorPropertyValue {
mbgl::Color color = self.mgl_color;
return {{ color.r, color.g, color.b, color.a }};
}
@end
+
+@implementation NSExpression (MGLColorAdditions)
+
++ (NSExpression *)mgl_expressionForRGBComponents:(NSArray<NSExpression *> *)components {
+ if (NSColor *color = [self mgl_colorWithComponentExpressions:components]) {
+ return [NSExpression expressionForConstantValue:color];
+ }
+
+ NSExpression *color = [NSExpression expressionForConstantValue:[NSColor class]];
+ NSExpression *alpha = [NSExpression expressionForConstantValue:@1.0];
+ return [NSExpression expressionForFunction:color
+ selectorName:@"colorWithRed:green:blue:alpha:"
+ arguments:[components arrayByAddingObject:alpha]];
+}
+
++ (NSExpression *)mgl_expressionForRGBAComponents:(NSArray<NSExpression *> *)components {
+ if (NSColor *color = [self mgl_colorWithComponentExpressions:components]) {
+ return [NSExpression expressionForConstantValue:color];
+ }
+
+ NSExpression *color = [NSExpression expressionForConstantValue:[NSColor class]];
+ return [NSExpression expressionForFunction:color
+ selectorName:@"colorWithRed:green:blue:alpha:"
+ arguments:components];
+}
+
+/**
+ Returns a color object corresponding to the given component expressions.
+ */
++ (NSColor *)mgl_colorWithComponentExpressions:(NSArray<NSExpression *> *)componentExpressions {
+ // Map the component expressions to constant components. If any component is
+ // a non-constant expression, the components cannot be converted into a
+ // constant color value.
+ std::vector<CGFloat> components;
+ for (NSExpression *componentExpression in componentExpressions) {
+ if (componentExpression.expressionType != NSConstantValueExpressionType) {
+ return nil;
+ }
+
+ NSNumber *component = (NSNumber *)componentExpression.constantValue;
+ if (![component isKindOfClass:[NSNumber class]]) {
+ return nil;
+ }
+
+ components.push_back(component.doubleValue / 255.0);
+ }
+ // Alpha
+ components.back() *= 255.0;
+
+ // macOS 10.12 Sierra and below uses calibrated RGB by default.
+ if ([NSColor redColor].colorSpaceName == NSCalibratedRGBColorSpace) {
+ return [NSColor colorWithCalibratedRed:components[0]
+ green:components[1]
+ blue:components[2]
+ alpha:components[3]];
+ }
+ // The Mapbox Style Specification does not specify a color space, but it is
+ // assumed to be sRGB for consistency with CSS.
+ return [NSColor colorWithColorSpace:[NSColorSpace sRGBColorSpace]
+ components:&components[0]
+ count:components.size()];
+}
+
+@end
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..215b06c7bf 100644
--- a/platform/node/DEVELOPING.md
+++ b/platform/node/DEVELOPING.md
@@ -1,14 +1,16 @@
# 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
-To develop these bindings, you’ll need to build them from source. Building requires [installing all of the basic dependencies needed for Mapbox GL Native](../../INSTALL.md), then running:
+To develop these bindings, you’ll need to build them from source. Building requires the prerequisites listed in either
+the [macOS](../macos/INSTALL.md#requirements) or [Linux](../linux/README.md#prerequisites) install documentation, depending
+on the target platform.
- npm install --build-from-source
+To compile the Node.js bindings and install module dependencies, from the repository root directory, run:
-From the root directory. This will compile the Node.js bindings and install module dependencies.
+ npm install --build-from-source
To recompile just the C++ code while developing, run `make node`.
diff --git a/platform/node/README.md b/platform/node/README.md
index d19b2a9343..ac5bcd7e8d 100644
--- a/platform/node/README.md
+++ b/platform/node/README.md
@@ -17,7 +17,8 @@ Run:
npm install @mapbox/mapbox-gl-native
```
-Other platforms will fall back to a source compile with `make node`; see INSTALL.md in the repository root directory for prequisites.
+Other platforms will fall back to a source compile with `make node`; see [DEVELOPING.md](DEVELOPING.md) for details on
+building from source.
## Testing
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..27866ccbed
--- /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 ? ParsingContext(*expected) : ParsingContext();
+ ParseResult parsed = ctx.parseLayerPropertyExpression(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..9b76f0f542 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;
};
@@ -58,6 +74,7 @@ void NodeMap::Init(v8::Local<v8::Object> target) {
Nan::SetPrototypeMethod(tpl, "removeLayer", RemoveLayer);
Nan::SetPrototypeMethod(tpl, "addImage", AddImage);
Nan::SetPrototypeMethod(tpl, "removeImage", RemoveImage);
+ Nan::SetPrototypeMethod(tpl, "setLayerZoomRange", SetLayerZoomRange);
Nan::SetPrototypeMethod(tpl, "setLayoutProperty", SetLayoutProperty);
Nan::SetPrototypeMethod(tpl, "setPaintProperty", SetPaintProperty);
Nan::SetPrototypeMethod(tpl, "setFilter", SetFilter);
@@ -65,6 +82,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 +272,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 +389,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 +414,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 +533,7 @@ void NodeMap::release() {
});
map.reset();
+ frontend.reset();
}
/**
@@ -508,7 +565,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
@@ -684,6 +741,33 @@ void NodeMap::RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>& info) {
nodeMap->map->getStyle().removeImage(*Nan::Utf8String(info[0]));
}
+
+void NodeMap::SetLayerZoomRange(const Nan::FunctionCallbackInfo<v8::Value>& info) {
+ using namespace mbgl::style;
+
+ auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder());
+ if (!nodeMap->map) return Nan::ThrowError(releasedMessage());
+
+ if (info.Length() != 3) {
+ return Nan::ThrowTypeError("Three arguments required");
+ }
+
+ if (!info[0]->IsString()) {
+ return Nan::ThrowTypeError("First argument must be a string");
+ }
+
+ if (!info[1]->IsNumber() || !info[2]->IsNumber()) {
+ return Nan::ThrowTypeError("Second and third arguments must be numbers");
+ }
+
+ mbgl::style::Layer* layer = nodeMap->map->getStyle().getLayer(*Nan::Utf8String(info[0]));
+ if (!layer) {
+ return Nan::ThrowTypeError("layer not found");
+ }
+
+ layer->setMinZoom(info[1]->NumberValue());
+ layer->setMaxZoom(info[2]->NumberValue());
+}
void NodeMap::SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>& info) {
using namespace mbgl::style;
@@ -709,7 +793,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 +825,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 +844,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 +967,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 +1147,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 +1164,7 @@ NodeMap::NodeMap(v8::Local<v8::Object> options)
pixelRatio,
*this,
threadpool,
- mbgl::MapMode::Still)),
+ mode)),
async(new uv_async_t) {
async->data = this;
@@ -1009,6 +1182,10 @@ NodeMap::~NodeMap() {
std::unique_ptr<mbgl::AsyncRequest> NodeMap::request(const mbgl::Resource& resource, mbgl::FileSource::Callback callback_) {
Nan::HandleScope scope;
+ // Because this method may be called while this NodeMap is already eligible for garbage collection,
+ // we need to explicitly hold onto our own handle here so that GC during a v8 call doesn't destroy
+ // *this while we're still executing code.
+ handle();
v8::Local<v8::Value> argv[] = {
Nan::New<v8::External>(this),
diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp
index c8e33bb347..7fe23ad86a 100644
--- a/platform/node/src/node_map.hpp
+++ b/platform/node/src/node_map.hpp
@@ -50,6 +50,7 @@ public:
static void RemoveLayer(const Nan::FunctionCallbackInfo<v8::Value>&);
static void AddImage(const Nan::FunctionCallbackInfo<v8::Value>&);
static void RemoveImage(const Nan::FunctionCallbackInfo<v8::Value>&);
+ static void SetLayerZoomRange(const Nan::FunctionCallbackInfo<v8::Value>&);
static void SetLayoutProperty(const Nan::FunctionCallbackInfo<v8::Value>&);
static void SetPaintProperty(const Nan::FunctionCallbackInfo<v8::Value>&);
static void SetFilter(const Nan::FunctionCallbackInfo<v8::Value>&);
@@ -57,6 +58,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 +76,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..a1d4d68511 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -1,59 +1,110 @@
{
+ "expression-tests/collator/accent-equals-de": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/accent-lt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/accent-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/base-default-locale": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/base-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/base-gt-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/case-lteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/case-not-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/case-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/comparison-number-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/diacritic-omitted-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/equals-non-string-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/non-object-error": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/variant-equals-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/collator/variant-gteq-en": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/is-supported-script/default": "This tests RTL text plugin behavior specific to GL JS",
+ "expression-tests/resolved-locale/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11692",
+ "expression-tests/to-string/basic": "https://github.com/mapbox/mapbox-gl-native/issues/11719",
"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/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/line-gradient/gradient": "https://github.com/mapbox/mapbox-gl-native/issues/11718",
+ "render-tests/line-gradient/translucent": "https://github.com/mapbox/mapbox-gl-native/issues/11718",
+ "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/real-world/bangkok": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/real-world/chicago": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
+ "render-tests/real-world/sanfrancisco": "https://github.com/mapbox/mapbox-gl-native/issues/10412",
"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#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-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..ce22816ef2 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -114,6 +114,7 @@ test('Map', function(t) {
'removeLayer',
'addImage',
'removeImage',
+ 'setLayerZoomRange',
'setLayoutProperty',
'setPaintProperty',
'setFilter',
@@ -121,6 +122,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..f083d4b519 100644
--- a/platform/qt/README.md
+++ b/platform/qt/README.md
@@ -1,41 +1,81 @@
-# Mapbox Qt SDK
+# Mapbox Maps SDK for Qt
-[![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 project using 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
-instructions](https://github.com/mapbox/mapbox-gl-native/tree/master/platform/linux#build),
-you also need:
+For Linux (tested on Ubuntu) desktop, together with these [build instructions](../linux/README.md), 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](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..390d89915a 100644
--- a/platform/qt/app/mapwindow.cpp
+++ b/platform/qt/app/mapwindow.cpp
@@ -22,6 +22,13 @@ MapWindow::MapWindow(const QMapboxGLSettings &settings)
setWindowIcon(QIcon(":icon.png"));
}
+MapWindow::~MapWindow()
+{
+ // Make sure we have a valid context so we
+ // can delete the QMapboxGL.
+ makeCurrent();
+}
+
void MapWindow::selfTest()
{
if (m_bearingAnimation) {
@@ -252,10 +259,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 +285,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 +316,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 +449,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/app/mapwindow.hpp b/platform/qt/app/mapwindow.hpp
index c484114ec0..6c05f03562 100644
--- a/platform/qt/app/mapwindow.hpp
+++ b/platform/qt/app/mapwindow.hpp
@@ -29,6 +29,7 @@ class MapWindow : public QGLWidget
public:
MapWindow(const QMapboxGLSettings &);
+ ~MapWindow();
void selfTest();
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..9d3b8a7ef4 100644
--- a/platform/qt/config.qdocconf
+++ b/platform/qt/config.qdocconf
@@ -4,10 +4,10 @@ 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
+description = Mapbox Maps SDK for Qt
language = Cpp
outputdir = docs
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..369890343f 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;
@@ -102,12 +112,16 @@ struct Q_DECL_EXPORT CustomLayerRenderParameters {
double zoom;
double bearing;
double pitch;
- double altitude;
+ double fieldOfView;
};
-typedef void (*CustomLayerInitializeFunction)(void* context) ;
-typedef void (*CustomLayerRenderFunction)(void* context, const CustomLayerRenderParameters&);
-typedef void (*CustomLayerDeinitializeFunction)(void* context);
+class Q_MAPBOXGL_EXPORT CustomLayerHostInterface {
+public:
+ virtual ~CustomLayerHostInterface() = default;
+ virtual void initialize() = 0;
+ virtual void render(const CustomLayerRenderParameters&) = 0;
+ virtual void deinitialize() = 0;
+};
} // namespace QMapbox
diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp
index e2fb283989..79eb672d1f 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)
@@ -113,6 +128,13 @@ public:
MapChangeSourceDidChange
};
+ enum MapLoadingFailure {
+ StyleParseFailure,
+ StyleLoadFailure,
+ NotFoundFailure,
+ UnknownFailure
+ };
+
// Determines the orientation of the map.
enum NorthOrientation {
NorthUpwards, // Default
@@ -185,8 +207,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;
@@ -209,10 +230,7 @@ public:
void removeImage(const QString &name);
void addCustomLayer(const QString &id,
- QMapbox::CustomLayerInitializeFunction,
- QMapbox::CustomLayerRenderFunction,
- QMapbox::CustomLayerDeinitializeFunction,
- void* context,
+ QScopedPointer<QMapbox::CustomLayerHostInterface>& host,
const QString& before = QString());
void addLayer(const QVariantMap &params, const QString& before = QString());
bool layerExists(const QString &id);
@@ -220,15 +238,28 @@ 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 mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason);
void copyrightsChanged(const QString &copyrightsHtml);
+ void staticRenderFinished(const QString &error);
+
private:
Q_DISABLE_COPY(QMapboxGL)
@@ -236,5 +267,6 @@ private:
};
Q_DECLARE_METATYPE(QMapboxGL::MapChange);
+Q_DECLARE_METATYPE(QMapboxGL::MapLoadingFailure);
#endif // QMAPBOXGL_H
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..2180f22d07 100644
--- a/platform/qt/src/qmapbox.cpp
+++ b/platform/qt/src/qmapbox.cpp
@@ -24,7 +24,7 @@ namespace QMapbox {
/*!
\namespace QMapbox
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
Contains miscellaneous Mapbox bindings used throughout QMapboxGL.
*/
@@ -74,7 +74,7 @@ namespace QMapbox {
/*!
\class QMapbox::Feature
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
Represents \l {https://www.mapbox.com/help/define-features/}{map features}
via its \a type (PointType, LineStringType or PolygonType), \a geometry, \a
@@ -94,7 +94,7 @@ namespace QMapbox {
/*!
\class QMapbox::ShapeAnnotationGeometry
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
Represents a shape annotation geometry.
*/
@@ -113,7 +113,7 @@ namespace QMapbox {
/*!
\class QMapbox::SymbolAnnotation
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
A symbol annotation comprises of its geometry and an icon identifier.
*/
@@ -121,7 +121,7 @@ namespace QMapbox {
/*!
\class QMapbox::LineAnnotation
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
Represents a line annotation object, along with its properties.
@@ -131,7 +131,7 @@ namespace QMapbox {
/*!
\class QMapbox::FillAnnotation
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
Represents a fill annotation object, along with its properties.
@@ -158,27 +158,9 @@ namespace QMapbox {
*/
/*!
- \typedef QMapbox::CustomLayerDeinitializeFunction
+ \class QMapbox::CustomLayerHostInterface
- Represents a callback to be called when destroying a custom layer.
-
- \warning This is used for delegating the rendering of a layer to the user of
- this API and is not officially supported. Use at your own risk.
-*/
-
-/*!
- \typedef QMapbox::CustomLayerInitializeFunction
-
- Represents a callback to be called when initializing a custom layer.
-
- \warning This is used for delegating the rendering of a layer to the user of
- this API and is not officially supported. Use at your own risk.
-*/
-
-/*!
- \typedef QMapbox::CustomLayerRenderFunction
-
- Represents a callback to be called on each render pass for a custom layer.
+ Represents a host interface to be implemented for rendering custom layers.
\warning This is used for delegating the rendering of a layer to the user of
this API and is not officially supported. Use at your own risk.
@@ -195,7 +177,7 @@ namespace QMapbox {
/*!
\class QMapbox::CustomLayerRenderParameters
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
QMapbox::CustomLayerRenderParameters provides the data passed on each render
pass for a custom layer.
@@ -206,7 +188,7 @@ namespace QMapbox {
Returns the current QMapbox::NetworkMode.
*/
-Q_DECL_EXPORT NetworkMode networkMode()
+NetworkMode networkMode()
{
return static_cast<NetworkMode>(mbgl::NetworkStatus::Get());
}
@@ -219,7 +201,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 +212,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..8c3355dc09 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");
@@ -75,15 +89,33 @@ QThreadStorage<std::shared_ptr<mbgl::util::RunLoop>> loop;
std::shared_ptr<mbgl::DefaultFileSource> sharedDefaultFileSource(
const std::string& cachePath, const std::string& assetRoot, uint64_t maximumCacheSize) {
- static std::weak_ptr<mbgl::DefaultFileSource> weak;
- auto fs = weak.lock();
+ static std::mutex mutex;
+ static std::unordered_map<std::string, std::weak_ptr<mbgl::DefaultFileSource>> fileSources;
+
+ std::lock_guard<std::mutex> lock(mutex);
- if (!fs) {
- weak = fs = std::make_shared<mbgl::DefaultFileSource>(
- cachePath, assetRoot, maximumCacheSize);
+ // Purge entries no longer in use.
+ for (auto it = fileSources.begin(); it != fileSources.end();) {
+ if (!it->second.lock()) {
+ it = fileSources.erase(it);
+ } else {
+ ++it;
+ }
}
- return fs;
+ // Return an existing FileSource if available.
+ auto sharedFileSource = fileSources.find(cachePath);
+ if (sharedFileSource != fileSources.end()) {
+ return sharedFileSource->second.lock();
+ }
+
+ // New path, create a new FileSource.
+ auto newFileSource = std::make_shared<mbgl::DefaultFileSource>(
+ cachePath, assetRoot, maximumCacheSize);
+
+ fileSources[cachePath] = newFileSource;
+
+ return newFileSource;
}
// Conversion helper functions.
@@ -100,8 +132,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(),
@@ -117,15 +154,14 @@ std::unique_ptr<mbgl::style::Image> toStyleImage(const QString &id, const QImage
\class QMapboxGLSettings
\brief The QMapboxGLSettings class stores the initial configuration for QMapboxGL.
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
QMapboxGLSettings is used to configure QMapboxGL at the moment of its creation.
Once created, the QMapboxGLSettings of a QMapboxGL can no longer be changed.
- Cache-related settings are shared between all QMapboxGL instances because different
- maps will share the same cache database file. The first map to configure cache properties
- such as size and path will force the configuration to all newly instantiated QMapboxGL
- objects.
+ Cache-related settings are shared between all QMapboxGL instances using the same cache path.
+ The first map to configure cache properties such as size will force the configuration
+ to all newly instantiated QMapboxGL objects using the same cache in the same process.
\since 4.7
*/
@@ -139,14 +175,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 +241,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 +272,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,10 +447,31 @@ 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.
- \inmodule Mapbox Qt SDK
+ \inmodule Mapbox Maps SDK for Qt
QMapboxGL is a Qt friendly version the Mapbox GL Native engine using Qt types
and deep integration with Qt event loop. QMapboxGL relies as much as possible
@@ -429,6 +535,19 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url)
*/
/*!
+ \enum QMapboxGL::MapLoadingFailure
+
+ This enum represents map loading failure type.
+
+ \value StyleParseFailure Failure to parse the style.
+ \value StyleLoadFailure Failure to load the style data.
+ \value NotFoundFailure Failure to obtain style resource file.
+ \value UnknownFailure Unknown map loading failure.
+
+ \sa mapLoadingFailed()
+*/
+
+/*!
\enum QMapboxGL::NorthOrientation
This enum sets the orientation of the north bearing. It will directly affect bearing when
@@ -850,7 +969,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 +1020,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 +1128,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);
}
/*!
@@ -1269,20 +1373,43 @@ void QMapboxGL::removeSource(const QString& id)
this API and is not officially supported. Use at your own risk.
*/
void QMapboxGL::addCustomLayer(const QString &id,
- QMapbox::CustomLayerInitializeFunction initFn,
- QMapbox::CustomLayerRenderFunction renderFn,
- QMapbox::CustomLayerDeinitializeFunction deinitFn,
- void *context,
+ QScopedPointer<QMapbox::CustomLayerHostInterface>& host,
const QString& before)
{
+ class HostWrapper : public mbgl::style::CustomLayerHost {
+ public:
+ QScopedPointer<QMapbox::CustomLayerHostInterface> ptr;
+ HostWrapper(QScopedPointer<QMapbox::CustomLayerHostInterface>& p)
+ : ptr(p.take()) {
+ }
+
+ void initialize() {
+ ptr->initialize();
+ }
+
+ void render(const mbgl::style::CustomLayerRenderParameters& params) {
+ QMapbox::CustomLayerRenderParameters renderParams;
+ renderParams.width = params.width;
+ renderParams.height = params.height;
+ renderParams.latitude = params.latitude;
+ renderParams.longitude = params.longitude;
+ renderParams.zoom = params.zoom;
+ renderParams.bearing = params.bearing;
+ renderParams.pitch = params.pitch;
+ renderParams.fieldOfView = params.fieldOfView;
+ ptr->render(renderParams);
+ }
+
+ void contextLost() { }
+
+ void deinitialize() {
+ ptr->deinitialize();
+ }
+ };
+
d_ptr->mapObj->getStyle().addLayer(std::make_unique<mbgl::style::CustomLayer>(
id.toStdString(),
- reinterpret_cast<mbgl::style::CustomLayerInitializeFunction>(initFn),
- // This cast is safe as long as both mbgl:: and QMapbox::
- // CustomLayerRenderParameters members remains the same.
- (mbgl::style::CustomLayerRenderFunction)renderFn,
- reinterpret_cast<mbgl::style::CustomLayerDeinitializeFunction>(deinitFn),
- context),
+ std::make_unique<HostWrapper>(host)),
before.isEmpty() ? mbgl::optional<std::string>() : mbgl::optional<std::string>(before.toStdString()));
}
@@ -1429,6 +1556,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 +1608,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 +1648,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
@@ -1479,6 +1666,13 @@ void QMapboxGL::connectionEstablished()
*/
/*!
+ \fn void QMapboxGL::mapLoadingFailed(QMapboxGL::MapLoadingFailure type, const QString &description)
+
+ This signal is emitted when a map loading failure happens. Details of the
+ failures are provided, including its \a type and textual \a description.
+*/
+
+/*!
\fn void QMapboxGL::copyrightsChanged(const QString &copyrightsHtml);
This signal is emitted when the copyrights of the current content of the map
@@ -1487,172 +1681,141 @@ 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(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString)), q, SIGNAL(mapLoadingFailed(QMapboxGL::MapLoadingFailure,QString)));
+ connect(m_mapObserver.get(), SIGNAL(copyrightsChanged(QString)), q, SIGNAL(copyrightsChanged(QString)));
+
+ // 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()));
- 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);
+ // 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..44cb8c41d5
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_observer.cpp
@@ -0,0 +1,120 @@
+#include "qmapboxgl_map_observer.hpp"
+
+#include "qmapboxgl_p.hpp"
+
+#include <mbgl/util/exception.hpp>
+
+#include <exception>
+
+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 exception)
+{
+ emit mapChanged(QMapboxGL::MapChangeDidFailLoadingMap);
+
+ QMapboxGL::MapLoadingFailure type;
+ QString description;
+
+ try {
+ std::rethrow_exception(exception);
+ } catch (const mbgl::util::StyleParseException& e) {
+ type = QMapboxGL::MapLoadingFailure::StyleParseFailure;
+ description = e.what();
+ } catch (const mbgl::util::StyleLoadException& e) {
+ type = QMapboxGL::MapLoadingFailure::StyleLoadFailure;
+ description = e.what();
+ } catch (const mbgl::util::NotFoundException& e) {
+ type = QMapboxGL::MapLoadingFailure::NotFoundFailure;
+ description = e.what();
+ } catch (const std::exception& e) {
+ type = QMapboxGL::MapLoadingFailure::UnknownFailure;
+ description = e.what();
+ }
+
+ emit mapLoadingFailed(type, description);
+}
+
+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::MapChangeWillStartRenderingMap);
+}
+
+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..98da5b6add
--- /dev/null
+++ b/platform/qt/src/qmapboxgl_map_observer.hpp
@@ -0,0 +1,46 @@
+#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 mapLoadingFailed(QMapboxGL::MapLoadingFailure, const QString &reason);
+ 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..4bcaea0e31 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>
@@ -23,11 +24,11 @@ namespace mapbox {
namespace sqlite {
// https://www.sqlite.org/rescode.html#ok
-static_assert(mbgl::underlying_type(Exception::OK) == 0, "error");
+static_assert(mbgl::underlying_type(ResultCode::OK) == 0, "error");
// https://www.sqlite.org/rescode.html#cantopen
-static_assert(mbgl::underlying_type(Exception::CANTOPEN) == 14, "error");
+static_assert(mbgl::underlying_type(ResultCode::CantOpen) == 14, "error");
// https://www.sqlite.org/rescode.html#notadb
-static_assert(mbgl::underlying_type(Exception::NOTADB) == 26, "error");
+static_assert(mbgl::underlying_type(ResultCode::NotADB) == 26, "error");
void checkQueryError(const QSqlQuery& query) {
QSqlError lastError = query.lastError();
@@ -56,24 +57,30 @@ void checkDatabaseOpenError(const QSqlDatabase &db) {
// always returns -1 for `nativeErrorCode()` on database errors.
QSqlError lastError = db.lastError();
if (lastError.type() != QSqlError::NoError) {
- throw Exception { Exception::Code::CANTOPEN, "Error opening the database." };
+ throw Exception { ResultCode::CantOpen, "Error opening the database." };
+ }
+}
+
+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." };
+ throw Exception { ResultCode::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,83 +177,91 @@ 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);
}
}
}
-Statement Database::prepare(const char *query) {
- return Statement(this, query);
-}
-
-Statement::Statement(Database *db, const char *sql)
- : impl(std::make_unique<StatementImpl>(QString(sql), *db->impl->db)) {
+Statement::Statement(Database& db, const char* sql)
+ : impl(std::make_unique<StatementImpl>(QString(sql),
+ QSqlDatabase::database(db.impl->connectionName))) {
assert(impl);
}
-Statement::Statement(Statement &&other)
- : impl(std::move(other.impl)) {
- assert(impl);
+Statement::~Statement() {
+#ifndef NDEBUG
+ // Crash if we're destructing this object while we know a Query object references this.
+ assert(!used);
+#endif
}
-Statement &Statement::operator=(Statement &&other) {
- assert(impl);
- std::swap(impl, other.impl);
- return *this;
+Query::Query(Statement& stmt_) : stmt(stmt_) {
+ assert(stmt.impl);
+
+#ifndef NDEBUG
+ assert(!stmt.used);
+ stmt.used = true;
+#endif
}
-Statement::~Statement() {
+Query::~Query() {
+ reset();
+ clearBindings();
+
+#ifndef NDEBUG
+ stmt.used = false;
+#endif
}
-template void Statement::bind(int, int64_t);
+template void Query::bind(int, int64_t);
template <typename T>
-void Statement::bind(int offset, T value) {
- assert(impl);
+void Query::bind(int offset, T value) {
+ assert(stmt.impl);
// Field numbering starts at 0.
- impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In);
- checkQueryError(impl->query);
+ stmt.impl->query.bindValue(offset - 1, QVariant::fromValue<T>(value), QSql::In);
+ checkQueryError(stmt.impl->query);
}
template <>
-void Statement::bind(int offset, std::nullptr_t) {
- assert(impl);
+void Query::bind(int offset, std::nullptr_t) {
+ assert(stmt.impl);
// Field numbering starts at 0.
- impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In);
- checkQueryError(impl->query);
+ stmt.impl->query.bindValue(offset - 1, QVariant(QVariant::Invalid), QSql::In);
+ checkQueryError(stmt.impl->query);
}
template <>
-void Statement::bind(int offset, int32_t value) {
+void Query::bind(int offset, int32_t value) {
bind(offset, static_cast<int64_t>(value));
}
template <>
-void Statement::bind(int offset, bool value) {
+void Query::bind(int offset, bool value) {
bind(offset, static_cast<int>(value));
}
template <>
-void Statement::bind(int offset, int8_t value) {
+void Query::bind(int offset, int8_t value) {
bind(offset, static_cast<int64_t>(value));
}
template <>
-void Statement::bind(int offset, uint8_t value) {
+void Query::bind(int offset, uint8_t value) {
bind(offset, static_cast<int64_t>(value));
}
template <>
-void Statement::bind(int offset, mbgl::Timestamp value) {
+void Query::bind(int offset, mbgl::Timestamp value) {
bind(offset, std::chrono::system_clock::to_time_t(value));
}
template <>
-void Statement::bind(int offset, optional<std::string> value) {
+void Query::bind(int offset, optional<std::string> value) {
if (value) {
bind(offset, *value);
} else {
@@ -254,7 +270,7 @@ void Statement::bind(int offset, optional<std::string> value) {
}
template <>
-void Statement::bind(int offset, optional<mbgl::Timestamp> value) {
+void Query::bind(int offset, optional<mbgl::Timestamp> value) {
if (value) {
bind(offset, *value);
} else {
@@ -262,145 +278,149 @@ void Statement::bind(int offset, optional<mbgl::Timestamp> value) {
}
}
-void Statement::bind(int offset, const char* value, std::size_t length, bool retain) {
- assert(impl);
+void Query::bind(int offset, const char* value, std::size_t length, bool /* retain */) {
+ assert(stmt.impl);
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) :
- QByteArray::fromRawData(value, length), QSql::In);
+ stmt.impl->query.bindValue(offset - 1, QString(QByteArray(value, length)), QSql::In);
- checkQueryError(impl->query);
+ checkQueryError(stmt.impl->query);
}
-void Statement::bind(int offset, const std::string& value, bool retain) {
+void Query::bind(int offset, const std::string& value, bool retain) {
bind(offset, value.data(), value.size(), retain);
}
-void Statement::bindBlob(int offset, const void* value_, std::size_t length, bool retain) {
+void Query::bindBlob(int offset, const void* value_, std::size_t length, bool retain) {
+ assert(stmt.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) :
+ stmt.impl->query.bindValue(offset - 1, retain ? QByteArray(value, length) :
QByteArray::fromRawData(value, length), QSql::In | QSql::Binary);
- checkQueryError(impl->query);
+ checkQueryError(stmt.impl->query);
}
-void Statement::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) {
+void Query::bindBlob(int offset, const std::vector<uint8_t>& value, bool retain) {
bindBlob(offset, value.data(), value.size(), retain);
}
-bool Statement::run() {
- assert(impl);
- if (impl->query.isValid()) {
- return impl->query.next();
- }
+bool Query::run() {
+ assert(stmt.impl);
- assert(!impl->query.isActive());
- impl->query.setForwardOnly(true);
- if (!impl->query.exec()) {
- checkQueryError(impl->query);
+ if (!stmt.impl->query.isValid()) {
+ if (stmt.impl->query.exec()) {
+ stmt.impl->lastInsertRowId = stmt.impl->query.lastInsertId().value<int64_t>();
+ stmt.impl->changes = stmt.impl->query.numRowsAffected();
+ } else {
+ checkQueryError(stmt.impl->query);
+ }
}
- impl->lastInsertRowId = impl->query.lastInsertId().value<int64_t>();
- impl->changes = impl->query.numRowsAffected();
+ const bool hasNext = stmt.impl->query.next();
+ if (!hasNext) stmt.impl->query.finish();
- return impl->query.next();
+ return hasNext;
}
-template bool Statement::get(int);
-template int Statement::get(int);
-template int64_t Statement::get(int);
-template double Statement::get(int);
+template bool Query::get(int);
+template int Query::get(int);
+template int64_t Query::get(int);
+template double Query::get(int);
-template <typename T> T Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QVariant value = impl->query.value(offset);
- checkQueryError(impl->query);
+template <typename T> T Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QVariant value = stmt.impl->query.value(offset);
+ checkQueryError(stmt.impl->query);
return value.value<T>();
}
-template <> std::vector<uint8_t> Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QByteArray byteArray = impl->query.value(offset).toByteArray();
- checkQueryError(impl->query);
+template <> std::vector<uint8_t> Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QByteArray byteArray = stmt.impl->query.value(offset).toByteArray();
+ checkQueryError(stmt.impl->query);
std::vector<uint8_t> blob(byteArray.begin(), byteArray.end());
return blob;
}
-template <> mbgl::Timestamp Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QVariant value = impl->query.value(offset);
- checkQueryError(impl->query);
+template <> mbgl::Timestamp Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QVariant value = stmt.impl->query.value(offset);
+ checkQueryError(stmt.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) {
- assert(impl && impl->query.isValid());
- QVariant value = impl->query.value(offset);
- checkQueryError(impl->query);
+template <> optional<int64_t> Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QVariant value = stmt.impl->query.value(offset);
+ checkQueryError(stmt.impl->query);
if (value.isNull())
return {};
return { value.value<int64_t>() };
}
-template <> optional<double> Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QVariant value = impl->query.value(offset);
- checkQueryError(impl->query);
+template <> optional<double> Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QVariant value = stmt.impl->query.value(offset);
+ checkQueryError(stmt.impl->query);
if (value.isNull())
return {};
return { value.value<double>() };
}
-template <> std::string Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QByteArray value = impl->query.value(offset).toByteArray();
- checkQueryError(impl->query);
+template <> std::string Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QByteArray value = stmt.impl->query.value(offset).toByteArray();
+ checkQueryError(stmt.impl->query);
return std::string(value.constData(), value.size());
}
-template <> optional<std::string> Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QByteArray value = impl->query.value(offset).toByteArray();
- checkQueryError(impl->query);
+template <> optional<std::string> Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QByteArray value = stmt.impl->query.value(offset).toByteArray();
+ checkQueryError(stmt.impl->query);
if (value.isNull())
return {};
return { std::string(value.constData(), value.size()) };
}
-template <> optional<mbgl::Timestamp> Statement::get(int offset) {
- assert(impl && impl->query.isValid());
- QVariant value = impl->query.value(offset);
- checkQueryError(impl->query);
+template <> optional<mbgl::Timestamp> Query::get(int offset) {
+ assert(stmt.impl && stmt.impl->query.isValid());
+ QVariant value = stmt.impl->query.value(offset);
+ checkQueryError(stmt.impl->query);
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() {
- assert(impl);
- impl->query.finish();
+void Query::reset() {
+ assert(stmt.impl);
+ stmt.impl->query.finish();
}
-void Statement::clearBindings() {
+void Query::clearBindings() {
// no-op
}
-int64_t Statement::lastInsertRowId() const {
- assert(impl);
- return impl->lastInsertRowId;
+int64_t Query::lastInsertRowId() const {
+ assert(stmt.impl);
+ return stmt.impl->lastInsertRowId;
}
-uint64_t Statement::changes() const {
- assert(impl);
- return (impl->changes < 0 ? 0 : impl->changes);
+uint64_t Query::changes() const {
+ assert(stmt.impl);
+ return (stmt.impl->changes < 0 ? 0 : stmt.impl->changes);
}
Transaction::Transaction(Database& db_, Mode mode)
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..6758793056 100755
--- a/scripts/generate-shaders.js
+++ b/scripts/generate-shaders.js
@@ -3,9 +3,12 @@
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');
+
+delete shaders.lineGradient;
+
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..dd9a127b70
--- /dev/null
+++ b/scripts/style-spec.js
@@ -0,0 +1,4 @@
+var spec = module.exports = require('../mapbox-gl-js/src/style-spec/reference/v8');
+
+// Make temporary modifications here when Native doesn't have all features that JS has.
+delete spec.paint_line['line-gradient'];
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..7d776f21c4 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 mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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..da87d13814 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 mat4& projMatrix) 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..fdd9558d0b 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>
@@ -17,51 +17,56 @@
namespace mbgl {
-FeatureIndex::FeatureIndex()
- : grid(util::EXTENT, 16, 0) {
+FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_)
+ : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) // 16x16 grid -> 32px cell
+ , tileData(std::move(tileData_)) {
}
void FeatureIndex::insert(const GeometryCollection& geometries,
std::size_t index,
const std::string& sourceLayerName,
- const std::string& bucketName) {
+ const std::string& bucketLeaderID) {
for (const auto& ring : geometries) {
- grid.insert(IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ },
- mapbox::geometry::envelope(ring));
+ auto envelope = mapbox::geometry::envelope(ring);
+ if (envelope.min.x < util::EXTENT &&
+ envelope.min.y < util::EXTENT &&
+ envelope.max.x >= 0 &&
+ envelope.max.y >= 0) {
+ grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++),
+ {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
+ }
}
}
-static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
- return a.sortIndex > b.sortIndex;
-}
-
-static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) {
- return a.sortIndex < b.sortIndex;
-}
-
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
- const float bearing,
+ const TransformState& transformState,
+ const mat4& posMatrix,
const double tileSize,
const double scale,
const RenderedQueryOptions& queryOptions,
- const GeometryTileData& geometryTileData,
- const CanonicalTileID& tileID,
+ const UnwrappedTileID& tileID,
const std::vector<const RenderLayer*>& layers,
- const CollisionTile* collisionTile,
- const float additionalQueryRadius) const {
+ const float additionalQueryPadding) const {
+
+ if (!tileData) {
+ return;
+ }
// Determine query radius
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
- const int16_t additionalRadius = std::min<int16_t>(util::EXTENT, additionalQueryRadius * pixelsToTileUnits);
+ const int16_t additionalPadding = std::min<int16_t>(util::EXTENT, additionalQueryPadding * pixelsToTileUnits);
// 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 - additionalPadding),
+ convertPoint<float>(box.max + additionalPadding) });
- std::sort(features.begin(), features.end(), topDown);
+ std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) {
+ return a.sortIndex > b.sortIndex;
+ });
size_t previousSortIndex = std::numeric_limits<size_t>::max();
for (const auto& indexedFeature : features) {
@@ -69,31 +74,59 @@ void FeatureIndex::query(
if (indexedFeature.sortIndex == previousSortIndex) continue;
previousSortIndex = indexedFeature.sortIndex;
- addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
+ addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix);
}
-
- // Query symbol features, if they've been placed.
- if (!collisionTile) {
- return;
+}
+
+std::unordered_map<std::string, std::vector<Feature>> FeatureIndex::lookupSymbolFeatures(const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& queryOptions,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const {
+ std::unordered_map<std::string, std::vector<Feature>> result;
+ if (!tileData) {
+ return result;
}
+ std::vector<IndexedSubfeature> sortedFeatures(symbolFeatures.begin(), symbolFeatures.end());
+
+ std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) {
+ // Same idea as the non-symbol sort order, but symbol features may have changed their sort order
+ // since their corresponding IndexedSubfeature was added to the CollisionIndex
+ // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort
+ if (featureSortOrder) {
+ // queryRenderedSymbols documentation says we'll return features in
+ // "top-to-bottom" rendering order (aka last-to-first).
+ // Actually there can be multiple symbol instances per feature, so
+ // we sort each feature based on the first matching symbol instance.
+ auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index);
+ auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index);
+ assert(sortedA != featureSortOrder->end());
+ assert(sortedB != featureSortOrder->end());
+ return sortedA > sortedB;
+ } else {
+ // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data"
+ // logic as non-symboles
+ return a.sortIndex > b.sortIndex;
+ }
+ });
- std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale);
- std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
- for (const auto& symbolFeature : symbolFeatures) {
- addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits);
+ for (const auto& symbolFeature : sortedFeatures) {
+ mat4 unusedMatrix;
+ addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix);
}
+ return result;
}
void FeatureIndex::addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature& indexedFeature,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
- const GeometryTileData& geometryTileData,
const CanonicalTileID& tileID,
const std::vector<const RenderLayer*>& layers,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const {
auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
for (const auto& layer : layers) {
@@ -108,14 +141,14 @@ void FeatureIndex::addFeature(
std::unique_ptr<GeometryTileLayer> sourceLayer;
std::unique_ptr<GeometryTileFeature> geometryTileFeature;
- for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) {
+ for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) {
const RenderLayer* renderLayer = getRenderLayer(layerID);
if (!renderLayer) {
continue;
}
if (!geometryTileFeature) {
- sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName);
+ sourceLayer = tileData->getLayer(indexedFeature.sourceLayerName);
assert(sourceLayer);
geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
@@ -123,11 +156,11 @@ void FeatureIndex::addFeature(
}
if (!renderLayer->is<RenderSymbolLayer>() &&
- !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) {
+ !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) {
continue;
}
- if (options.filter && !(*options.filter)(*geometryTileFeature)) {
+ if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast<float>(tileID.z), geometryTileFeature.get() })) {
continue;
}
@@ -157,8 +190,8 @@ optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry(
return translated;
}
-void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs) {
- bucketLayerIDs[bucketName] = layerIDs;
+void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs) {
+ bucketLayerIDs[bucketLeaderID] = layerIDs;
}
} // namespace mbgl
diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp
index 2ae7da33df..739c1f282f 100644
--- a/src/mbgl/geometry/feature_index.hpp
+++ b/src/mbgl/geometry/feature_index.hpp
@@ -2,8 +2,10 @@
#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>
+#include <mbgl/util/mat4.hpp>
#include <vector>
#include <string>
@@ -13,37 +15,57 @@ namespace mbgl {
class RenderedQueryOptions;
class RenderLayer;
+class TransformState;
-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_))
+ , bucketLeaderID(std::move(bucketName_))
+ , sortIndex(sortIndex_)
+ , bucketInstanceId(0)
+ {}
+
+
+ IndexedSubfeature(const IndexedSubfeature& other, uint32_t bucketInstanceId_)
+ : index(other.index)
+ , sourceLayerName(other.sourceLayerName)
+ , bucketLeaderID(other.bucketLeaderID)
+ , sortIndex(other.sortIndex)
+ , bucketInstanceId(bucketInstanceId_)
+ {}
+ size_t index;
std::string sourceLayerName;
- std::string bucketName;
+ std::string bucketLeaderID;
size_t sortIndex;
+
+ // Only used for symbol features
+ uint32_t bucketInstanceId;
};
class FeatureIndex {
public:
- FeatureIndex();
+ FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_);
- void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName);
+ const GeometryTileData* getData() { return tileData.get(); }
+
+ void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, const std::string& bucketLeaderID);
void query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCoordinates& queryGeometry,
- const float bearing,
+ const TransformState&,
+ const mat4& posMatrix,
const double tileSize,
const double scale,
const RenderedQueryOptions& options,
- const GeometryTileData&,
- const CanonicalTileID&,
+ const UnwrappedTileID&,
const std::vector<const RenderLayer*>&,
- const CollisionTile*,
- const float additionalQueryRadius) const;
+ const float additionalQueryPadding) const;
static optional<GeometryCoordinates> translateQueryGeometry(
const GeometryCoordinates& queryGeometry,
@@ -52,23 +74,31 @@ public:
const float bearing,
const float pixelsToTileUnits);
- void setBucketLayerIDs(const std::string& bucketName, const std::vector<std::string>& layerIDs);
+ void setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs);
+
+ std::unordered_map<std::string, std::vector<Feature>> lookupSymbolFeatures(
+ const std::vector<IndexedSubfeature>& symbolFeatures,
+ const RenderedQueryOptions& options,
+ const std::vector<const RenderLayer*>& layers,
+ const OverscaledTileID& tileID,
+ const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const;
private:
void addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature&,
- const GeometryCoordinates& queryGeometry,
const RenderedQueryOptions& options,
- const GeometryTileData&,
const CanonicalTileID&,
const std::vector<const RenderLayer*>&,
- const float bearing,
- const float pixelsToTileUnits) const;
+ const GeometryCoordinates& queryGeometry,
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const;
GridIndex<IndexedSubfeature> grid;
unsigned int sortIndex = 0;
std::unordered_map<std::string, std::vector<std::string>> bucketLayerIDs;
+ std::unique_ptr<const GeometryTileData> tileData;
};
} // namespace mbgl
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 4b77954d12..ba44adb42b 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);
@@ -489,10 +521,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));
@@ -503,11 +535,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));
}
@@ -607,19 +639,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..7dfa8edf43 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,38 +18,38 @@ 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_),
- hasText(shapedTextOrientations.first || shapedTextOrientations.second),
+ hasText(false),
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);
+ }
+ // 'hasText' depends on finding at least one glyph in the shaping that's also in the GlyphPositionMap
+ hasText = horizontalGlyphQuads.size() > 0 || verticalGlyphQuads.size() > 0;
if (shapedTextOrientations.first && shapedTextOrientations.second) {
writingModes = WritingModeType::Horizontal | WritingModeType::Vertical;
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..b2f6fd450f 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()),
+ : bucketLeaderID(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) {
@@ -101,7 +100,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
const size_t featureCount = sourceLayer->featureCount();
for (size_t i = 0; i < featureCount; ++i) {
auto feature = sourceLayer->getFeature(i);
- if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
+ if (!leader.filter(expression::EvaluationContext { this->zoom, feature.get() }))
continue;
SymbolFeature ft(std::move(feature));
@@ -127,7 +126,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
if (hasText) {
std::string u8string = layout.evaluate<TextField>(zoom, ft);
- if (layout.get<TextField>().isConstant()) {
+ if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) {
u8string = util::replaceTokens(u8string, getValue);
}
@@ -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);
}
}
}
@@ -157,7 +159,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
if (hasIcon) {
std::string icon = layout.evaluate<IconImage>(zoom, ft);
- if (layout.get<IconImage>().isConstant()) {
+ if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().isExpression()) {
icon = util::replaceTokens(icon, getValue);
}
ft.icon = icon;
@@ -183,18 +185,20 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
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;
@@ -288,12 +292,9 @@ 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(), bucketLeaderID, symbolInstances.size());
auto addSymbolInstance = [&] (const GeometryCoordinates& line, Anchor& anchor) {
// https://github.com/mapbox/vector-tile-spec/tree/master/2.1#41-layers
@@ -314,14 +315,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 +393,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, bucketLeaderID, 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 +489,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 +510,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 +526,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 +548,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..43b577859f 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;
@@ -37,13 +36,16 @@ public:
void prepare(const GlyphMap&, const GlyphPositions&,
const ImageMap&, const ImagePositions&);
- 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 bucketLeaderID;
+ std::vector<SymbolInstance> symbolInstances;
+
private:
void addFeature(const size_t,
const SymbolFeature&,
@@ -54,23 +56,19 @@ private:
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 +85,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..ef669c6e19 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,66 @@ 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) {
+ if (symbol.glyphOffsets.empty()) {
+ assert(false);
+ return optional<std::pair<PlacedGlyph, PlacedGlyph>>();
+ }
+
+ 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 +289,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 +298,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 +336,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 +353,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 +362,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 +378,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 +411,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..d81544eed5 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;
}
@@ -446,7 +446,6 @@ CameraOptions Map::cameraForGeometry(const Geometry<double>& geometry, const Edg
latLngs.push_back({ pt.y, pt.x });
});
return cameraForLatLngs(latLngs, padding, bearing);
-
}
LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const {
@@ -617,6 +616,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 {
@@ -719,6 +747,11 @@ void Map::Impl::onInvalidate() {
}
void Map::Impl::onUpdate() {
+ // Don't load/render anything in still mode until explicitly requested.
+ if (mode != MapMode::Continuous && !stillImageRequest) {
+ return;
+ }
+
TimePoint timePoint = mode == MapMode::Continuous ? Clock::now() : Clock::time_point::max();
transform.updateTransitions(timePoint);
@@ -765,7 +798,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..a85b251fb4 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
@@ -397,4 +421,17 @@ float TransformState::getCameraToTileDistance(const UnwrappedTileID& tileID) con
return projectedCenter[3];
}
+float TransformState::maxPitchScaleFactor() const {
+ if (size.isEmpty()) {
+ return {};
+ }
+ auto latLng = screenCoordinateToLatLng({ 0, static_cast<float>(getSize().height) });
+ mat4 mat = coordinatePointMatrix(getZoom());
+ Point<double> pt = Projection::project(latLng, scale) / double(util::tileSize);
+ vec4 p = {{ pt.x, pt.y, 0, 1 }};
+ vec4 topPoint;
+ matrix::transformMat4(topPoint, p, mat);
+ return topPoint[3] / getCameraToCenterDistance();
+}
+
} // namespace mbgl
diff --git a/src/mbgl/map/transform_state.hpp b/src/mbgl/map/transform_state.hpp
index 59522d89fd..b6f8ae4424 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;
@@ -87,6 +87,7 @@ public:
}
float getCameraToTileDistance(const UnwrappedTileID&) const;
+ float maxPitchScaleFactor() const;
private:
bool rotatedNorth() const;
@@ -134,6 +135,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..9b5037ed9f 100644
--- a/src/mbgl/programs/symbol_program.hpp
+++ b/src/mbgl/programs/symbol_program.hpp
@@ -61,8 +61,8 @@ struct SymbolLayoutAttributes : gl::Attributes<
{{
static_cast<int16_t>(labelAnchor.x),
static_cast<int16_t>(labelAnchor.y),
- static_cast<int16_t>(::round(o.x * 64)), // use 1/64 pixels for placement
- static_cast<int16_t>(::round((o.y + glyphOffsetY) * 64))
+ static_cast<int16_t>(::round(o.x * 32)), // use 1/32 pixels for placement
+ static_cast<int16_t>(::round((o.y + glyphOffsetY) * 32))
}},
{{
tx,
@@ -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..4fe03eb453 100644
--- a/src/mbgl/renderer/buckets/symbol_bucket.cpp
+++ b/src/mbgl/renderer/buckets/symbol_bucket.cpp
@@ -16,10 +16,16 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::PossiblyEvaluated layo
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons_,
- bool iconsNeedLinear_)
+ bool iconsNeedLinear_,
+ bool sortFeaturesByY_,
+ const std::string bucketName_,
+ const std::vector<SymbolInstance>&& symbolInstances_)
: layout(std::move(layout_)),
sdfIcons(sdfIcons_),
iconsNeedLinear(iconsNeedLinear_ || iconSize.isDataDriven() || !iconSize.isZoomConstant()),
+ sortFeaturesByY(sortFeaturesByY_),
+ bucketLeaderID(std::move(bucketName_)),
+ symbolInstances(std::move(symbolInstances_)),
textSizeBinder(SymbolSizeBinder::create(zoom, textSize, TextSize::defaultValue())),
iconSizeBinder(SymbolSizeBinder::create(zoom, iconSize, IconSize::defaultValue())) {
@@ -36,28 +42,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 +138,87 @@ 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();
+
+ featureSortOrder = std::make_unique<std::vector<size_t>>();
+ featureSortOrder->reserve(symbolInstanceIndexes.size());
+
+ for (auto i : symbolInstanceIndexes) {
+ const SymbolInstance& symbolInstance = symbolInstances[i];
+ featureSortOrder->push_back(symbolInstance.featureIndex);
+
+ 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..e4aaf5ba30 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,36 @@ public:
const style::DataDrivenPropertyValue<float>& iconSize,
float zoom,
bool sdfIcons,
- bool iconsNeedLinear);
+ bool iconsNeedLinear,
+ bool sortFeaturesByY,
+ const std::string bucketLeaderID,
+ 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;
+
+ const std::string bucketLeaderID;
+
+ 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 +85,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 +101,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 +109,33 @@ 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;
+
+ std::shared_ptr<std::vector<size_t>> featureSortOrder;
};
} // 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..44c3fffb6c 100644
--- a/src/mbgl/renderer/layers/render_background_layer.cpp
+++ b/src/mbgl/renderer/layers/render_background_layer.cpp
@@ -5,8 +5,9 @@
#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>
+#include <mbgl/map/transform_state.hpp>
namespace mbgl {
@@ -46,12 +47,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 +60,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 +79,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 +87,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..56fccfe071 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(),
@@ -93,28 +93,75 @@ void RenderCircleLayer::render(PaintParameters& parameters, RenderSource*) {
}
}
+GeometryCoordinate projectPoint(const GeometryCoordinate& p, const mat4& posMatrix, const Size& size) {
+ vec4 pos = {{ static_cast<double>(p.x), static_cast<double>(p.y), 0, 1 }};
+ matrix::transformMat4(pos, pos, posMatrix);
+ return {
+ static_cast<int16_t>((static_cast<float>(pos[0] / pos[3]) + 1) * size.width * 0.5),
+ static_cast<int16_t>((static_cast<float>(pos[1] / pos[3]) + 1) * size.height * 0.5)
+ };
+}
+
+GeometryCoordinates projectQueryGeometry(const GeometryCoordinates& queryGeometry, const mat4& posMatrix, const Size& size) {
+ GeometryCoordinates projectedGeometry;
+ for (auto& p : queryGeometry) {
+ projectedGeometry.push_back(projectPoint(p, posMatrix, size));
+ }
+ return projectedGeometry;
+}
+
bool RenderCircleLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4& posMatrix) const {
// Translate query geometry
- auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
+ const GeometryCoordinates& translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::CircleTranslate>(),
evaluated.get<style::CircleTranslateAnchor>(),
- bearing,
- pixelsToTileUnits);
-
- // Evaluate function
- auto circleRadius = evaluated.get<style::CircleRadius>()
- .evaluate(feature, zoom, style::CircleRadius::defaultValue())
- * pixelsToTileUnits;
+ transformState.getAngle(),
+ pixelsToTileUnits).value_or(queryGeometry);
+
+ // Evaluate functions
+ auto radius = evaluated.evaluate<style::CircleRadius>(zoom, feature);
+ auto stroke = evaluated.evaluate<style::CircleStrokeWidth>(zoom, feature);
+ auto size = radius + stroke;
+
+ // For pitch-alignment: map, compare feature geometry to query geometry in the plane of the tile
+ // Otherwise, compare geometry in the plane of the viewport
+ // A circle with fixed scaling relative to the viewport gets larger in tile space as it moves into the distance
+ // A circle with fixed scaling relative to the map gets smaller in viewport space as it moves into the distance
+ bool alignWithMap = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature) == AlignmentType::Map;
+ const GeometryCoordinates& transformedQueryGeometry = alignWithMap ?
+ translatedQueryGeometry :
+ projectQueryGeometry(translatedQueryGeometry, posMatrix, transformState.getSize());
+ auto transformedSize = alignWithMap ? size * pixelsToTileUnits : size;
+
+ auto geometry = feature.getGeometries();
+ for (auto& ring : geometry) {
+ for (auto& point : ring) {
+ const GeometryCoordinate& transformedPoint = alignWithMap ? point : projectPoint(point, posMatrix, transformState.getSize());
+
+ float adjustedSize = transformedSize;
+ vec4 center = {{ static_cast<double>(point.x), static_cast<double>(point.y), 0, 1 }};
+ matrix::transformMat4(center, center, posMatrix);
+ auto pitchScale = evaluated.evaluate<style::CirclePitchScale>(zoom, feature);
+ auto pitchAlignment = evaluated.evaluate<style::CirclePitchAlignment>(zoom, feature);
+ if (pitchScale == CirclePitchScaleType::Viewport && pitchAlignment == AlignmentType::Map) {
+ adjustedSize *= center[3] / transformState.getCameraToCenterDistance();
+ } else if (pitchScale == CirclePitchScaleType::Map && pitchAlignment == AlignmentType::Viewport) {
+ adjustedSize *= transformState.getCameraToCenterDistance() / center[3];
+ }
+
+ if (util::polygonIntersectsBufferedPoint(transformedQueryGeometry, transformedPoint, adjustedSize)) return true;
+ }
+ }
- // Test intersection
- return util::polygonIntersectsBufferedMultiPoint(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries(), circleRadius);
+ return false;
}
} // namespace mbgl
diff --git a/src/mbgl/renderer/layers/render_circle_layer.hpp b/src/mbgl/renderer/layers/render_circle_layer.hpp
index f31715f98f..c9eeae4652 100644
--- a/src/mbgl/renderer/layers/render_circle_layer.hpp
+++ b/src/mbgl/renderer/layers/render_circle_layer.hpp
@@ -20,8 +20,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_custom_layer.cpp b/src/mbgl/renderer/layers/render_custom_layer.cpp
index a429b8d82e..be9f64d9eb 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.cpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.cpp
@@ -6,23 +6,24 @@
#include <mbgl/style/layers/custom_layer_impl.hpp>
#include <mbgl/map/transform_state.hpp>
#include <mbgl/gl/gl.hpp>
+#include <mbgl/util/mat4.hpp>
namespace mbgl {
using namespace style;
RenderCustomLayer::RenderCustomLayer(Immutable<style::CustomLayer::Impl> _impl)
- : RenderLayer(LayerType::Custom, _impl) {
+ : RenderLayer(LayerType::Custom, _impl), host(_impl->host) {
+ assert(BackendScope::exists());
+ host->initialize();
}
RenderCustomLayer::~RenderCustomLayer() {
assert(BackendScope::exists());
- if (initialized) {
- if (contextDestroyed && impl().contextLostFn ) {
- impl().contextLostFn(impl().context);
- } else if (!contextDestroyed && impl().deinitializeFn) {
- impl().deinitializeFn(impl().context);
- }
+ if (contextDestroyed) {
+ host->contextLost();
+ } else {
+ host->deinitialize();
}
}
@@ -44,15 +45,13 @@ std::unique_ptr<Bucket> RenderCustomLayer::createBucket(const BucketParameters&,
}
void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*) {
- if (context != impl().context || !initialized) {
+ if (host != impl().host) {
//If the context changed, deinitialize the previous one before initializing the new one.
- if (context && !contextDestroyed && impl().deinitializeFn) {
- MBGL_CHECK_ERROR(impl().deinitializeFn(context));
+ if (host && !contextDestroyed) {
+ MBGL_CHECK_ERROR(host->deinitialize());
}
- context = impl().context;
- assert(impl().initializeFn);
- MBGL_CHECK_ERROR(impl().initializeFn(impl().context));
- initialized = true;
+ host = impl().host;
+ MBGL_CHECK_ERROR(host->initialize());
}
gl::Context& glContext = paintParameters.context;
@@ -74,9 +73,11 @@ void RenderCustomLayer::render(PaintParameters& paintParameters, RenderSource*)
parameters.bearing = -state.getAngle() * util::RAD2DEG;
parameters.pitch = state.getPitch();
parameters.fieldOfView = state.getFieldOfView();
+ mat4 projMatrix;
+ state.getProjMatrix(projMatrix);
+ parameters.projectionMatrix = projMatrix;
- assert(impl().renderFn);
- MBGL_CHECK_ERROR(impl().renderFn(context, parameters));
+ MBGL_CHECK_ERROR(host->render(parameters));
// Reset the view back to our original one, just in case the CustomLayer changed
// the viewport or Framebuffer.
diff --git a/src/mbgl/renderer/layers/render_custom_layer.hpp b/src/mbgl/renderer/layers/render_custom_layer.hpp
index 6d1fea99d3..971d8d8f42 100644
--- a/src/mbgl/renderer/layers/render_custom_layer.hpp
+++ b/src/mbgl/renderer/layers/render_custom_layer.hpp
@@ -24,9 +24,8 @@ public:
};
private:
- bool initialized = false;
bool contextDestroyed = false;
- void * context = nullptr;
+ std::shared_ptr<style::CustomLayerHost> host;
};
template <>
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
index fbd6160e8a..871464223c 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
@@ -151,14 +151,15 @@ bool RenderFillExtrusionLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::FillExtrusionTranslate>(),
evaluated.get<style::FillExtrusionTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
diff --git a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
index 838494cf91..f7ba13c267 100644
--- a/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
@@ -22,8 +22,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
diff --git a/src/mbgl/renderer/layers/render_fill_layer.cpp b/src/mbgl/renderer/layers/render_fill_layer.cpp
index 22cb9563c1..efd3f4215c 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.cpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.cpp
@@ -188,14 +188,15 @@ bool RenderFillLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::FillTranslate>(),
evaluated.get<style::FillTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
return util::polygonIntersectsMultiPolygon(translatedQueryGeometry.value_or(queryGeometry), feature.getGeometries());
diff --git a/src/mbgl/renderer/layers/render_fill_layer.hpp b/src/mbgl/renderer/layers/render_fill_layer.hpp
index a51865698f..bd195fb828 100644
--- a/src/mbgl/renderer/layers/render_fill_layer.hpp
+++ b/src/mbgl/renderer/layers/render_fill_layer.hpp
@@ -20,8 +20,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
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..72c60446aa
--- /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 (!parameters.context.supportsHalfFloatTextures || !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 TransformState&,
+ const float pixelsToTileUnits,
+ const mat4&) const {
+ (void) queryGeometry;
+ (void) feature;
+ (void) zoom;
+ (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..29fad7d8b8
--- /dev/null
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.hpp
@@ -0,0 +1,49 @@
+#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 TransformState&,
+ const float,
+ const mat4&) 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_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index 1b4a1c0ff7..02f61af0fa 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -162,15 +162,16 @@ bool RenderLineLayer::queryIntersectsFeature(
const GeometryCoordinates& queryGeometry,
const GeometryTileFeature& feature,
const float zoom,
- const float bearing,
- const float pixelsToTileUnits) const {
+ const TransformState& transformState,
+ const float pixelsToTileUnits,
+ const mat4&) const {
// Translate query geometry
auto translatedQueryGeometry = FeatureIndex::translateQueryGeometry(
queryGeometry,
evaluated.get<style::LineTranslate>(),
evaluated.get<style::LineTranslateAnchor>(),
- bearing,
+ transformState.getAngle(),
pixelsToTileUnits);
// Evaluate function
diff --git a/src/mbgl/renderer/layers/render_line_layer.hpp b/src/mbgl/renderer/layers/render_line_layer.hpp
index 8bf7e2329d..5d5d79c044 100644
--- a/src/mbgl/renderer/layers/render_line_layer.hpp
+++ b/src/mbgl/renderer/layers/render_line_layer.hpp
@@ -29,8 +29,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const override;
+ const mat4&) const override;
std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const override;
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..04a1608564 100644
--- a/src/mbgl/renderer/render_layer.hpp
+++ b/src/mbgl/renderer/render_layer.hpp
@@ -4,6 +4,7 @@
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layer_type.hpp>
#include <mbgl/tile/geometry_tile_data.hpp>
+#include <mbgl/util/mat4.hpp>
#include <memory>
#include <string>
@@ -17,6 +18,7 @@ class PropertyEvaluationParameters;
class PaintParameters;
class RenderSource;
class RenderTile;
+class TransformState;
class RenderLayer {
protected:
@@ -69,8 +71,9 @@ public:
const GeometryCoordinates&,
const GeometryTileFeature&,
const float,
+ const TransformState&,
const float,
- const float) const { return false; };
+ const mat4&) const { return false; };
virtual std::unique_ptr<Bucket> createBucket(const BucketParameters&, const std::vector<const RenderLayer*>&) const = 0;
@@ -82,13 +85,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..dc80cb1dc6 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 mat4& projMatrix) 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..ded07a0909 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,12 +84,7 @@ void Renderer::Impl::setObserver(RendererObserver* observer_) {
}
void Renderer::Impl::render(const UpdateParameters& updateParameters) {
- if (updateParameters.mode == MapMode::Still) {
- // Don't load/render anyting in still mode until explicitly requested.
- if (!updateParameters.stillImageRequest) {
- return;
- }
-
+ if (updateParameters.mode != MapMode::Continuous) {
// Reset zoom history state.
zoomHistory.first = true;
}
@@ -183,6 +181,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 +255,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 +290,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 +304,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 +340,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 +358,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 +373,50 @@ 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);
+ 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 +425,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 +464,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 +482,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 +499,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 +634,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) {
@@ -632,6 +664,39 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
return queryRenderedFeatures(geometry, options, layers);
}
+
+void Renderer::Impl::queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const {
+
+ auto renderedSymbols = placement->getCollisionIndex().queryRenderedSymbols(geometry);
+ std::vector<std::reference_wrapper<const RetainedQueryData>> bucketQueryData;
+ for (auto entry : renderedSymbols) {
+ bucketQueryData.push_back(placement->getQueryData(entry.first));
+ }
+ // Although symbol query is global, symbol results are only sortable within a bucket
+ // For a predictable global sort order, we sort the buckets based on their corresponding tile position
+ std::sort(bucketQueryData.begin(), bucketQueryData.end(), [](const RetainedQueryData& a, const RetainedQueryData& b) {
+ return
+ std::tie(a.tileID.canonical.z, a.tileID.canonical.y, a.tileID.wrap, a.tileID.canonical.x) <
+ std::tie(b.tileID.canonical.z, b.tileID.canonical.y, b.tileID.wrap, b.tileID.canonical.x);
+ });
+
+ for (auto wrappedQueryData : bucketQueryData) {
+ auto& queryData = wrappedQueryData.get();
+ auto bucketSymbols = queryData.featureIndex->lookupSymbolFeatures(renderedSymbols[queryData.bucketInstanceId],
+ options,
+ layers,
+ queryData.tileID,
+ queryData.featureSortOrder);
+
+ for (auto layer : bucketSymbols) {
+ auto& resultFeatures = resultsByLayer[layer.first];
+ std::move(layer.second.begin(), layer.second.end(), std::inserter(resultFeatures, resultFeatures.end()));
+ }
+ }
+}
std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineString& geometry, const RenderedQueryOptions& options, const std::vector<const RenderLayer*>& layers) const {
std::unordered_set<std::string> sourceIDs;
@@ -639,13 +704,18 @@ std::vector<Feature> Renderer::Impl::queryRenderedFeatures(const ScreenLineStrin
sourceIDs.emplace(layer->baseImpl->source);
}
+ mat4 projMatrix;
+ transformState.getProjMatrix(projMatrix);
+
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, projMatrix);
std::move(sourceResults.begin(), sourceResults.end(), std::inserter(resultsByLayer, resultsByLayer.begin()));
}
}
+
+ queryRenderedSymbols(resultsByLayer, geometry, layers, options);
std::vector<Feature> result;
@@ -690,12 +760,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 +792,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 +803,30 @@ bool Renderer::Impl::hasTransitions() const {
}
}
+ if (placement->hasTransitions(timePoint)) {
+ return true;
+ }
+
+ if (fadingTiles) {
+ return true;
+ }
+
return false;
}
+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..4124f6f416 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,18 +53,23 @@ 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;
RenderLayer* getRenderLayer(const std::string& id);
const RenderLayer* getRenderLayer(const std::string& id) const;
-
+
+ void queryRenderedSymbols(std::unordered_map<std::string, std::vector<Feature>>& resultsByLayer,
+ const ScreenLineString& geometry,
+ const std::vector<const RenderLayer*>& layers,
+ const RenderedQueryOptions& options) const;
+
std::vector<Feature> queryRenderedFeatures(const ScreenLineString&, const RenderedQueryOptions&, const std::vector<const RenderLayer*>&) const;
// GlyphManagerObserver implementation.
@@ -72,6 +79,8 @@ private:
void onTileChanged(RenderSource&, const OverscaledTileID&) override;
void onTileError(RenderSource&, const OverscaledTileID&, std::exception_ptr) override;
+ void updateFadingTiles();
+
friend class Renderer;
RendererBackend& backend;
@@ -91,7 +100,6 @@ private:
};
RenderState renderState = RenderState::Never;
- FrameHistory frameHistory;
ZoomHistory zoomHistory;
TransformState transformState;
@@ -108,7 +116,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..2d28b8dd84
--- /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 mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
+}
+
+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..5533fe2b83
--- /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 mat4& projMatrix) 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..0e265efff4 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 mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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..297fa09a29 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 mat4& projMatrix) 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..5c497e8144 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 mat4&) 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..cf14e180fd 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 mat4& projMatrix) 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..fbf2c09d19
--- /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 mat4&) 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..48c7803e92
--- /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 mat4& projMatrix) 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..f97ce4e65b 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 mat4&) 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..32539a046d 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 mat4& projMatrix) 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..4de4f01e3f 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 mat4& projMatrix) const {
+ return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options, projMatrix);
}
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..6fd2425aa3 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 mat4& projMatrix) 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 da4ca4abf0..d28e95181b 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) {
@@ -149,7 +155,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) {
@@ -162,8 +168,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();
@@ -175,6 +190,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 =
@@ -207,20 +234,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 mat4& projMatrix) const {
std::unordered_map<std::string, std::vector<Feature>> result;
if (renderTiles.empty() || geometry.empty()) {
return result;
@@ -242,14 +264,19 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
std::tie(b.id.canonical.z, b.id.canonical.y, b.id.wrap, b.id.canonical.x);
});
+ auto maxPitchScaleFactor = transformState.maxPitchScaleFactor();
+
for (const RenderTile& renderTile : sortedTiles) {
+ const float scale = std::pow(2, transformState.getZoom() - renderTile.id.canonical.z);
+ auto queryPadding = maxPitchScaleFactor * renderTile.tile.getQueryPadding(layers) * util::EXTENT / util::tileSize / scale;
+
GeometryCoordinate tileSpaceBoundsMin = TileCoordinate::toGeometryCoordinate(renderTile.id, box.min);
- if (tileSpaceBoundsMin.x >= util::EXTENT || tileSpaceBoundsMin.y >= util::EXTENT) {
+ if (tileSpaceBoundsMin.x - queryPadding >= util::EXTENT || tileSpaceBoundsMin.y - queryPadding >= util::EXTENT) {
continue;
}
GeometryCoordinate tileSpaceBoundsMax = TileCoordinate::toGeometryCoordinate(renderTile.id, box.max);
- if (tileSpaceBoundsMax.x < 0 || tileSpaceBoundsMax.y < 0) {
+ if (tileSpaceBoundsMax.x + queryPadding < 0 || tileSpaceBoundsMax.y + queryPadding < 0) {
continue;
}
@@ -263,7 +290,8 @@ std::unordered_map<std::string, std::vector<Feature>> TilePyramid::queryRendered
tileSpaceQueryGeometry,
transformState,
layers,
- options);
+ options,
+ projMatrix);
}
return result;
@@ -283,7 +311,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..0cef9e2c40 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 mat4& projMatrix) 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..bc5d9bc6f9 100644
--- a/src/mbgl/shaders/collision_box.cpp
+++ b/src/mbgl/shaders/collision_box.cpp
@@ -10,77 +10,53 @@ 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 = clamp(
+ 0.5 + 0.5 * (u_camera_to_center_distance / camera_to_anchor_distance),
+ 0.0, // Prevents oversized near-field boxes in pitched/overzoomed tiles
+ 4.0);
- 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/line.cpp b/src/mbgl/shaders/line.cpp
index c700295a15..68d2dcc468 100644
--- a/src/mbgl/shaders/line.cpp
+++ b/src/mbgl/shaders/line.cpp
@@ -31,6 +31,7 @@ uniform vec2 u_gl_units_to_pixels;
varying vec2 v_normal;
varying vec2 v_width2;
varying float v_gamma_scale;
+varying highp float v_linesofar;
#ifndef HAS_UNIFORM_u_color
@@ -131,6 +132,8 @@ void main() {
vec2 a_extrude = a_data.xy - 128.0;
float a_direction = mod(a_data.z, 4.0) - 1.0;
+ v_linesofar = (floor(a_data.z / 4.0) + a_data.w * 64.0) * 2.0;
+
vec2 pos = a_pos_normal.xy;
// x is 1 if it's a round cap, 0 otherwise
diff --git a/src/mbgl/shaders/line_pattern.cpp b/src/mbgl/shaders/line_pattern.cpp
index f8d785ade9..be88255e3c 100644
--- a/src/mbgl/shaders/line_pattern.cpp
+++ b/src/mbgl/shaders/line_pattern.cpp
@@ -215,8 +215,14 @@ void main() {
float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0);
float x_b = mod(v_linesofar / u_pattern_size_b.x, 1.0);
- float y_a = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_a.y);
- float y_b = 0.5 + (v_normal.y * v_width2.s / u_pattern_size_b.y);
+
+ // v_normal.y is 0 at the midpoint of the line, -1 at the lower edge, 1 at the upper edge
+ // we clamp the line width outset to be between 0 and half the pattern height plus padding (2.0)
+ // to ensure we don't sample outside the designated symbol on the sprite sheet.
+ // 0.5 is added to shift the component to be bounded between 0 and 1 for interpolation of
+ // the texture coordinate
+ float y_a = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_a.y + 2.0) / 2.0) / u_pattern_size_a.y);
+ float y_b = 0.5 + (v_normal.y * clamp(v_width2.s, 0.0, (u_pattern_size_b.y + 2.0) / 2.0) / u_pattern_size_b.y);
vec2 pos_a = mix(u_pattern_tl_a / u_texsize, u_pattern_br_a / u_texsize, vec2(x_a, y_a));
vec2 pos_b = mix(u_pattern_tl_b / u_texsize, u_pattern_br_b / u_texsize, vec2(x_b, y_b));
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..c037c81005 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) {
@@ -81,7 +80,10 @@ void main() {
highp float distance_ratio = u_pitch_with_map ?
camera_to_anchor_distance / u_camera_to_center_distance :
u_camera_to_center_distance / camera_to_anchor_distance;
- highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+ highp float perspective_ratio = clamp(
+ 0.5 + 0.5 * distance_ratio,
+ 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
+ 4.0);
size *= perspective_ratio;
@@ -103,22 +105,17 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
- gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
v_tex = a_tex / u_texsize;
- // 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 +126,7 @@ uniform lowp float u_opacity;
varying vec2 v_tex;
-varying vec2 v_fade_tex;
+varying float v_fade_opacity;
void main() {
@@ -138,7 +135,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..b584c00315 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) {
@@ -157,7 +156,10 @@ void main() {
highp float distance_ratio = u_pitch_with_map ?
camera_to_anchor_distance / u_camera_to_center_distance :
u_camera_to_center_distance / camera_to_anchor_distance;
- highp float perspective_ratio = 0.5 + 0.5 * distance_ratio;
+ highp float perspective_ratio = clamp(
+ 0.5 + 0.5 * distance_ratio,
+ 0.0, // Prevents oversized near-field symbols in pitched/overzoomed tiles
+ 4.0);
size *= perspective_ratio;
@@ -181,35 +183,16 @@ void main() {
mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos);
vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0);
- gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 64.0 * fontScale), 0.0, 1.0);
+ gl_Position = u_gl_coord_matrix * vec4(projected_pos.xy / projected_pos.w + rotation_matrix * (a_offset / 32.0 * fontScale), 0.0, 1.0);
float gamma_scale = gl_Position.w;
vec2 tex = a_tex / u_texsize;
- // 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 +238,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 +272,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 +288,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..3c941945fd
--- /dev/null
+++ b/src/mbgl/style/conversion/filter.cpp
@@ -0,0 +1,304 @@
+#include <mbgl/style/conversion/filter.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+using GeometryValue = mapbox::geometry::value;
+
+// This is a port from https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/feature_filter/index.js
+static bool isExpressionFilter(const Convertible& filter) {
+ if (!isArray(filter) || arrayLength(filter) == 0) {
+ return false;
+ }
+
+ optional<std::string> op = toString(arrayMember(filter, 0));
+
+ if (!op) {
+ return false;
+
+ } else if (*op == "has") {
+ if (arrayLength(filter) < 2) return false;
+ optional<std::string> operand = toString(arrayMember(filter, 1));
+ return operand && *operand != "$id" && *operand != "$type";
+
+ } else if (*op == "in" || *op == "!in" || *op == "!has" || *op == "none") {
+ return false;
+
+ } else if (*op == "==" || *op == "!=" || *op == ">" || *op == ">=" || *op == "<" || *op == "<=") {
+ return arrayLength(filter) == 3 && (isArray(arrayMember(filter, 1)) || isArray(arrayMember(filter, 2)));
+
+ } else if (*op == "any" || *op == "all") {
+ for (std::size_t i = 1; i < arrayLength(filter); i++) {
+ Convertible f = arrayMember(filter, i);
+ if (!isExpressionFilter(f) && !toBool(f)) {
+ return false;
+ }
+ }
+ return true;
+
+ } else {
+ return true;
+ }
+}
+
+static optional<GeometryValue> normalizeValue(const optional<GeometryValue>& 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<GeometryValue> 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<GeometryValue> 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<GeometryValue> 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<GeometryValue> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ optional<GeometryValue> 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> convertExpressionFilter(const Convertible& value, Error& error) {
+ expression::ParsingContext ctx(expression::type::Boolean);
+ expression::ParseResult expression = ctx.parseExpression(value);
+ if (!expression) {
+ error = { ctx.getCombinedErrors() };
+ return {};
+ }
+
+ return { ExpressionFilter { std::move(*expression) } };
+}
+
+optional<Filter> Converter<Filter>::operator()(const Convertible& value, Error& error) const {
+ if (isExpressionFilter(value)) {
+ return convertExpressionFilter(value, error);
+ }
+
+ 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/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp
index 6ae6fede42..7b7727d7c4 100644
--- a/src/mbgl/style/conversion/stringify.hpp
+++ b/src/mbgl/style/conversion/stringify.hpp
@@ -225,6 +225,10 @@ public:
void operator()(const NotHasIdentifierFilter&) {
stringifyUnaryFilter("!has", "$id");
}
+
+ void operator()(const ExpressionFilter& filter) {
+ stringify(writer, filter.expression->serialize());
+ }
private:
template <class F>
@@ -286,138 +290,19 @@ void stringify(Writer& writer, const Undefined&) {
writer.Null();
}
-template <class Writer>
-void stringify(Writer& writer, const CategoricalValue& v) {
- CategoricalValue::visit(v, [&] (const auto& v_) { stringify(writer, v_); });
-}
-
-template <class Writer>
-class StringifyStops {
-public:
- Writer& writer;
-
- template <class T>
- void operator()(const ExponentialStops<T>& f) {
- writer.Key("type");
- writer.String("exponential");
- writer.Key("base");
- writer.Double(f.base);
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const IntervalStops<T>& f) {
- writer.Key("type");
- writer.String("interval");
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const CategoricalStops<T>& f) {
- writer.Key("type");
- writer.String("categorical");
- writer.Key("stops");
- stringifyStops(f.stops);
- }
-
- template <class T>
- void operator()(const IdentityStops<T>&) {
- writer.Key("type");
- writer.String("identity");
- }
-
- template <class T>
- void operator()(const CompositeExponentialStops<T>& f) {
- writer.Key("type");
- writer.String("exponential");
- writer.Key("base");
- writer.Double(f.base);
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
- template <class T>
- void operator()(const CompositeIntervalStops<T>& f) {
- writer.Key("type");
- writer.String("interval");
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
- template <class T>
- void operator()(const CompositeCategoricalStops<T>& f) {
- writer.Key("type");
- writer.String("categorical");
- writer.Key("stops");
- stringifyCompositeStops(f.stops);
- }
-
-private:
- template <class K, class V>
- void stringifyStops(const std::map<K, V>& stops) {
- writer.StartArray();
- for (const auto& stop : stops) {
- writer.StartArray();
- stringify(writer, stop.first);
- stringify(writer, stop.second);
- writer.EndArray();
- }
- writer.EndArray();
- }
-
- template <class InnerStops>
- void stringifyCompositeStops(const std::map<float, InnerStops>& stops) {
- writer.StartArray();
- for (const auto& outer : stops) {
- for (const auto& inner : outer.second) {
- writer.StartArray();
- writer.StartObject();
- writer.Key("zoom");
- writer.Double(outer.first);
- writer.Key("value");
- stringify(writer, inner.first);
- writer.EndObject();
- stringify(writer, inner.second);
- writer.EndArray();
- }
- }
- writer.EndArray();
- }
-};
-
template <class Writer, class T>
-void stringify(Writer& writer, const CameraFunction<T>& f) {
- writer.StartObject();
- CameraFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- writer.EndObject();
+void stringify(Writer& writer, const CameraFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
-void stringify(Writer& writer, const SourceFunction<T>& f) {
- writer.StartObject();
- writer.Key("property");
- writer.String(f.property);
- SourceFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- if (f.defaultValue) {
- writer.Key("default");
- stringify(writer, *f.defaultValue);
- }
- writer.EndObject();
+void stringify(Writer& writer, const SourceFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
-void stringify(Writer& writer, const CompositeFunction<T>& f) {
- writer.StartObject();
- writer.Key("property");
- writer.String(f.property);
- CompositeFunction<T>::Stops::visit(f.stops, StringifyStops<Writer> { writer });
- if (f.defaultValue) {
- writer.Key("default");
- stringify(writer, *f.defaultValue);
- }
- writer.EndObject();
+void stringify(Writer& writer, const CompositeFunction<T>& fn) {
+ stringify(writer, fn.getExpression().serialize());
}
template <class Writer, class T>
diff --git a/src/mbgl/style/conversion/tileset.cpp b/src/mbgl/style/conversion/tileset.cpp
index 6e559c0cac..a2c4aa80b3 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);
@@ -93,6 +103,8 @@ optional<Tileset> Converter<Tileset>::operator()(const Convertible& value, Error
error = { "bounds left longitude should be less than right longitude" };
return {};
}
+ *left = util::max(-180.0, *left);
+ *right = util::min(180.0, *right);
result.bounds = LatLngBounds::hull({ *bottom, *left }, { *top, *right });
}
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..725e5ddb51
--- /dev/null
+++ b/src/mbgl/style/expression/at.cpp
@@ -0,0 +1,70 @@
+#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) {
+ return EvaluationError {
+ "Array index out of bounds: " + util::toString(i) + " < 0."
+ };
+ }
+
+ if (i >= inputArray.size()) {
+ return EvaluationError {
+ "Array index out of bounds: " + util::toString(i) +
+ " > " + util::toString(inputArray.size() - 1) + "."
+ };
+ }
+ if (i != std::floor(i)) {
+ return EvaluationError {
+ "Array index must be an integer, but found " + util::toString(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..c257dbf2bb
--- /dev/null
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -0,0 +1,554 @@
+#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/i18n.hpp>
+#include <mbgl/util/ignore.hpp>
+#include <mbgl/util/string.hpp>
+#include <mbgl/util/platform.hpp>
+#include <cmath>
+
+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 color.toArray();
+ });
+
+ 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("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("round", [](double x) -> Result<double> { return std::round(x); });
+ define("floor", [](double x) -> Result<double> { return std::floor(x); });
+ define("ceil", [](double x) -> Result<double> { return std::ceil(x); });
+ define("abs", [](double x) -> Result<double> { return std::abs(x); });
+
+ 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("is-supported-script", [](const std::string& x) -> Result<bool> {
+ return util::i18n::isStringInSupportedScript(x);
+ });
+
+ 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..daad8523f2
--- /dev/null
+++ b/src/mbgl/style/expression/interpolate.cpp
@@ -0,0 +1,249 @@
+#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) {
+ if (exponential.base == 1) {
+ serialized.emplace_back(std::vector<mbgl::Value>{{ std::string("linear") }});
+ } else {
+ 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/length.cpp b/src/mbgl/style/expression/length.cpp
new file mode 100644
index 0000000000..258353ae4e
--- /dev/null
+++ b/src/mbgl/style/expression/length.cpp
@@ -0,0 +1,66 @@
+#include <mbgl/style/expression/length.hpp>
+#include <mbgl/util/string.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+Length::Length(std::unique_ptr<Expression> input_)
+ : Expression(type::Number),
+ input(std::move(input_)) {
+}
+
+EvaluationResult Length::evaluate(const EvaluationContext& params) const {
+ const EvaluationResult value = input->evaluate(params);
+ if (!value) return value;
+ return value->match(
+ [] (const std::string& s) {
+ return EvaluationResult { double(s.size()) };
+ },
+ [] (const std::vector<Value>& v) {
+ return EvaluationResult { double(v.size()) };
+ },
+ [&] (const auto&) -> EvaluationResult {
+ return EvaluationError { "Expected value to be of type string or array, but found " + toString(typeOf(*value)) + " instead." };
+ });
+}
+
+void Length::eachChild(const std::function<void(const Expression&)>& visit) const {
+ visit(*input);
+}
+
+bool Length::operator==(const Expression& e) const {
+ if (auto eq = dynamic_cast<const Length*>(&e)) {
+ return *eq->input == *input;
+ }
+ return false;
+}
+
+std::vector<optional<Value>> Length::possibleOutputs() const {
+ return { nullopt };
+}
+
+using namespace mbgl::style::conversion;
+ParseResult Length::parse(const Convertible& value, ParsingContext& ctx) {
+ std::size_t length = arrayLength(value);
+
+ if (length != 2) {
+ ctx.error("Expected one argument, but found " + util::toString(length) + " instead.");
+ return ParseResult();
+ }
+
+ ParseResult input = ctx.parse(arrayMember(value, 1), 1);
+ if (!input) return ParseResult();
+
+ type::Type type = (*input)->getType();
+ if (!type.is<type::Array>() && !type.is<type::StringType>() && !type.is<type::ValueType>()) {
+ ctx.error("Expected argument of type string or array, but found " + toString(type) + " instead.");
+ return ParseResult();
+ }
+
+ return ParseResult(std::make_unique<Length>(std::move(*input)));
+}
+
+} // 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..b522aeff9a
--- /dev/null
+++ b/src/mbgl/style/expression/parsing_context.cpp
@@ -0,0 +1,256 @@
+
+#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/length.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 (auto varExpression = dynamic_cast<const Var*>(&expression)) {
+ return isConstant(*varExpression->getBoundExpression());
+ }
+
+ if (auto compound = dynamic_cast<const CompoundExpressionBase*>(&expression)) {
+ if (compound->getName() == "error") {
+ return false;
+ }
+ }
+
+ bool isTypeAnnotation = dynamic_cast<const Coercion*>(&expression) ||
+ dynamic_cast<const Assertion*>(&expression) ||
+ dynamic_cast<const ArrayAssertion*>(&expression);
+
+ bool childrenConstant = true;
+ expression.eachChild([&](const Expression& child) {
+ // We can _almost_ assume that if `expressions` children are constant,
+ // they would already have been evaluated to Literal values when they
+ // were parsed. Type annotations are the exception, because they might
+ // have been inferred and added after a child was parsed.
+
+ // So we recurse into isConstant() for the children of type annotations,
+ // but otherwise simply check whether they are Literals.
+ if (isTypeAnnotation) {
+ childrenConstant = childrenConstant && isConstant(child);
+ } else {
+ childrenConstant = childrenConstant && dynamic_cast<const Literal*>(&child);
+ }
+ });
+ if (!childrenConstant) {
+ 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},
+ {"length", Length::parse},
+ {"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;
+ }
+
+ auto array = [&](std::unique_ptr<Expression> expression) {
+ std::vector<std::unique_ptr<Expression>> args;
+ args.push_back(std::move(expression));
+ return args;
+ };
+
+ if (expected) {
+ const type::Type actual = (*parsed)->getType();
+ if ((*expected == type::String || *expected == type::Number || *expected == type::Boolean || *expected == type::Object) && 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 constant, 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));
+ }
+ }
+
+ return parsed;
+}
+
+ParseResult ParsingContext::parseExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
+ return parse(value, typeAnnotationOption);
+}
+
+ParseResult ParsingContext::parseLayerPropertyExpression(const Convertible& value, TypeAnnotationOption typeAnnotationOption) {
+ ParseResult parsed = parse(value, typeAnnotationOption);
+ if (parsed && !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;
+}
+
+const std::string ParsingContext::getCombinedErrors() const {
+ std::string combinedError;
+ for (const ParsingError& parsingError : *errors) {
+ if (combinedError.size() > 0) {
+ combinedError += "\n";
+ }
+ if (parsingError.key.size() > 0) {
+ combinedError += parsingError.key + ": ";
+ }
+ combinedError += parsingError.message;
+ }
+ return combinedError;
+}
+
+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..1b3257c755
--- /dev/null
+++ b/src/mbgl/style/expression/value.cpp
@@ -0,0 +1,354 @@
+#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 {
+ std::array<double, 4> array = color.toArray();
+ return std::vector<mbgl::Value>{
+ std::string("rgba"),
+ array[0],
+ array[1],
+ array[2],
+ array[3],
+ };
+ },
+ [&](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/filter.cpp b/src/mbgl/style/filter.cpp
new file mode 100644
index 0000000000..51aa6bcf82
--- /dev/null
+++ b/src/mbgl/style/filter.cpp
@@ -0,0 +1,13 @@
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+
+bool Filter::operator()(const expression::EvaluationContext &context) const {
+ return FilterBase::visit(*this, FilterEvaluator { context });
+}
+
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/filter_evaluator.cpp b/src/mbgl/style/filter_evaluator.cpp
new file mode 100644
index 0000000000..72022172f4
--- /dev/null
+++ b/src/mbgl/style/filter_evaluator.cpp
@@ -0,0 +1,225 @@
+#include <mbgl/style/filter.hpp>
+#include <mbgl/style/filter_evaluator.hpp>
+#include <mbgl/tile/geometry_tile_data.hpp>
+
+namespace mbgl {
+namespace style {
+
+template <class Op>
+struct Comparator {
+ const Op& op;
+
+ template <class T>
+ bool operator()(const T& lhs, const T& rhs) const {
+ return op(lhs, rhs);
+ }
+
+ template <class T0, class T1>
+ auto operator()(const T0& lhs, const T1& rhs) const
+ -> typename std::enable_if_t<std::is_arithmetic<T0>::value && !std::is_same<T0, bool>::value &&
+ std::is_arithmetic<T1>::value && !std::is_same<T1, bool>::value, bool> {
+ return op(double(lhs), double(rhs));
+ }
+
+ template <class T0, class T1>
+ auto operator()(const T0&, const T1&) const
+ -> typename std::enable_if_t<!std::is_arithmetic<T0>::value || std::is_same<T0, bool>::value ||
+ !std::is_arithmetic<T1>::value || std::is_same<T1, bool>::value, bool> {
+ return false;
+ }
+
+ bool operator()(const NullValue&,
+ const NullValue&) const {
+ // Should be unreachable; null is not currently allowed by the style specification.
+ assert(false);
+ return false;
+ }
+
+ bool operator()(const std::vector<Value>&,
+ const std::vector<Value>&) const {
+ // Should be unreachable; nested values are not currently allowed by the style specification.
+ assert(false);
+ return false;
+ }
+
+ bool operator()(const PropertyMap&,
+ const PropertyMap&) const {
+ // Should be unreachable; nested values are not currently allowed by the style specification.
+ assert(false);
+ return false;
+ }
+};
+
+template <class Op>
+bool compare(const Value& lhs, const Value& rhs, const Op& op) {
+ return Value::binary_visit(lhs, rhs, Comparator<Op> { op });
+}
+
+bool equal(const Value& lhs, const Value& rhs) {
+ return compare(lhs, rhs, [] (const auto& lhs_, const auto& rhs_) { return lhs_ == rhs_; });
+}
+
+bool FilterEvaluator::operator()(const NullFilter&) const {
+ return true;
+}
+
+bool FilterEvaluator::operator()(const EqualsFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return actual && equal(*actual, filter.value);
+}
+
+bool FilterEvaluator::operator()(const NotEqualsFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return !actual || !equal(*actual, filter.value);
+}
+
+bool FilterEvaluator::operator()(const LessThanFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; });
+}
+
+bool FilterEvaluator::operator()(const LessThanEqualsFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; });
+}
+
+bool FilterEvaluator::operator()(const GreaterThanFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; });
+}
+
+bool FilterEvaluator::operator()(const GreaterThanEqualsFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; });
+}
+
+bool FilterEvaluator::operator()(const InFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ if (!actual)
+ return false;
+ for (const auto& v: filter.values) {
+ if (equal(*actual, v)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FilterEvaluator::operator()(const NotInFilter& filter) const {
+ optional<Value> actual = context.feature->getValue(filter.key);
+ if (!actual)
+ return true;
+ for (const auto& v: filter.values) {
+ if (equal(*actual, v)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilterEvaluator::operator()(const AnyFilter& filter) const {
+ for (const auto& f: filter.filters) {
+ if (Filter::visit(f, *this)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FilterEvaluator::operator()(const AllFilter& filter) const {
+ for (const auto& f: filter.filters) {
+ if (!Filter::visit(f, *this)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilterEvaluator::operator()(const NoneFilter& filter) const {
+ for (const auto& f: filter.filters) {
+ if (Filter::visit(f, *this)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilterEvaluator::operator()(const HasFilter& filter) const {
+ return bool(context.feature->getValue(filter.key));
+}
+
+bool FilterEvaluator::operator()(const NotHasFilter& filter) const {
+ return !context.feature->getValue(filter.key);
+}
+
+bool FilterEvaluator::operator()(const TypeEqualsFilter& filter) const {
+ return context.feature->getType() == filter.value;
+}
+
+bool FilterEvaluator::operator()(const TypeNotEqualsFilter& filter) const {
+ return context.feature->getType() != filter.value;
+}
+
+bool FilterEvaluator::operator()(const TypeInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (context.feature->getType() == v) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FilterEvaluator::operator()(const TypeNotInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (context.feature->getType() == v) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilterEvaluator::operator()(const IdentifierEqualsFilter& filter) const {
+ return context.feature->getID() == filter.value;
+}
+
+bool FilterEvaluator::operator()(const IdentifierNotEqualsFilter& filter) const {
+ return context.feature->getID() != filter.value;
+}
+
+bool FilterEvaluator::operator()(const IdentifierInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (context.feature->getID() == v) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool FilterEvaluator::operator()(const IdentifierNotInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (context.feature->getID() == v) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FilterEvaluator::operator()(const HasIdentifierFilter&) const {
+ return bool(context.feature->getID());
+}
+
+bool FilterEvaluator::operator()(const NotHasIdentifierFilter&) const {
+ return !context.feature->getID();
+}
+
+bool FilterEvaluator::operator()(const ExpressionFilter& filter) const {
+ const expression::EvaluationResult result = filter.expression->evaluate(context);
+ if (result) {
+ const optional<bool> typed = expression::fromExpressionValue<bool>(*result);
+ return typed ? *typed : false;
+ }
+ return false;
+}
+
+} // 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/background_layer.cpp b/src/mbgl/style/layers/background_layer.cpp
index d4ead18816..66ab46c078 100644
--- a/src/mbgl/style/layers/background_layer.cpp
+++ b/src/mbgl/style/layers/background_layer.cpp
@@ -53,12 +53,14 @@ void BackgroundLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void BackgroundLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/circle_layer.cpp b/src/mbgl/style/layers/circle_layer.cpp
index 9854932699..6dd744df1f 100644
--- a/src/mbgl/style/layers/circle_layer.cpp
+++ b/src/mbgl/style/layers/circle_layer.cpp
@@ -81,12 +81,14 @@ void CircleLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void CircleLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/custom_layer.cpp b/src/mbgl/style/layers/custom_layer.cpp
index 854c771847..0e51a70e50 100644
--- a/src/mbgl/style/layers/custom_layer.cpp
+++ b/src/mbgl/style/layers/custom_layer.cpp
@@ -6,20 +6,8 @@ namespace mbgl {
namespace style {
CustomLayer::CustomLayer(const std::string& layerID,
- CustomLayerInitializeFunction init,
- CustomLayerRenderFunction render,
- CustomLayerContextLostFunction contextLost,
- CustomLayerDeinitializeFunction deinit,
- void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, contextLost, deinit, context)) {
-}
-
-CustomLayer::CustomLayer(const std::string& layerID,
- CustomLayerInitializeFunction init,
- CustomLayerRenderFunction render,
- CustomLayerDeinitializeFunction deinit,
- void* context)
- : Layer(makeMutable<Impl>(layerID, init, render, nullptr, deinit, context)) {
+ std::unique_ptr<CustomLayerHost> host)
+ : Layer(makeMutable<Impl>(layerID, std::move(host))) {
}
CustomLayer::~CustomLayer() = default;
diff --git a/src/mbgl/style/layers/custom_layer_impl.cpp b/src/mbgl/style/layers/custom_layer_impl.cpp
index 1de268d2e2..05c41623c4 100644
--- a/src/mbgl/style/layers/custom_layer_impl.cpp
+++ b/src/mbgl/style/layers/custom_layer_impl.cpp
@@ -4,17 +4,9 @@ namespace mbgl {
namespace style {
CustomLayer::Impl::Impl(const std::string& id_,
- CustomLayerInitializeFunction initializeFn_,
- CustomLayerRenderFunction renderFn_,
- CustomLayerContextLostFunction contextLostFn_,
- CustomLayerDeinitializeFunction deinitializeFn_,
- void* context_)
+ std::unique_ptr<CustomLayerHost> host_)
: Layer::Impl(LayerType::Custom, id_, std::string()) {
- initializeFn = initializeFn_;
- renderFn = renderFn_;
- deinitializeFn = deinitializeFn_;
- contextLostFn = contextLostFn_;
- context = context_;
+ host = std::move(host_);
}
bool CustomLayer::Impl::hasLayoutDifference(const Layer::Impl&) const {
diff --git a/src/mbgl/style/layers/custom_layer_impl.hpp b/src/mbgl/style/layers/custom_layer_impl.hpp
index 62efbbe15b..a41962c276 100644
--- a/src/mbgl/style/layers/custom_layer_impl.hpp
+++ b/src/mbgl/style/layers/custom_layer_impl.hpp
@@ -3,6 +3,8 @@
#include <mbgl/style/layer_impl.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
+#include <memory>
+
namespace mbgl {
class TransformState;
@@ -12,20 +14,12 @@ namespace style {
class CustomLayer::Impl : public Layer::Impl {
public:
Impl(const std::string& id,
- CustomLayerInitializeFunction,
- CustomLayerRenderFunction,
- CustomLayerContextLostFunction,
- CustomLayerDeinitializeFunction,
- void* context);
+ std::unique_ptr<CustomLayerHost> host);
bool hasLayoutDifference(const Layer::Impl&) const override;
void stringifyLayout(rapidjson::Writer<rapidjson::StringBuffer>&) const override;
- CustomLayerInitializeFunction initializeFn = nullptr;
- CustomLayerRenderFunction renderFn = nullptr;
- CustomLayerContextLostFunction contextLostFn = nullptr;
- CustomLayerDeinitializeFunction deinitializeFn = nullptr;
- void* context = nullptr;
+ std::shared_ptr<CustomLayerHost> host;
};
} // namespace style
diff --git a/src/mbgl/style/layers/fill_extrusion_layer.cpp b/src/mbgl/style/layers/fill_extrusion_layer.cpp
index 62f92cef75..c5b4ef0ef3 100644
--- a/src/mbgl/style/layers/fill_extrusion_layer.cpp
+++ b/src/mbgl/style/layers/fill_extrusion_layer.cpp
@@ -81,12 +81,14 @@ void FillExtrusionLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void FillExtrusionLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/fill_layer.cpp b/src/mbgl/style/layers/fill_layer.cpp
index 65975752db..99a2a51ed0 100644
--- a/src/mbgl/style/layers/fill_layer.cpp
+++ b/src/mbgl/style/layers/fill_layer.cpp
@@ -81,12 +81,14 @@ void FillLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void FillLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/heatmap_layer.cpp b/src/mbgl/style/layers/heatmap_layer.cpp
new file mode 100644
index 0000000000..3f7881ddd3
--- /dev/null
+++ b/src/mbgl/style/layers/heatmap_layer.cpp
@@ -0,0 +1,241 @@
+// 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_);
+ observer->onLayerChanged(*this);
+}
+
+void HeatmapLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// 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..e352ae090c
--- /dev/null
+++ b/src/mbgl/style/layers/hillshade_layer.cpp
@@ -0,0 +1,240 @@
+// 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_);
+ observer->onLayerChanged(*this);
+}
+
+void HillshadeLayer::setMaxZoom(float maxZoom) {
+ auto impl_ = mutableImpl();
+ impl_->maxZoom = maxZoom;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
+
+// 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..6d748311bf 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;
@@ -102,12 +108,14 @@ void <%- camelize(type) %>Layer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void <%- camelize(type) %>Layer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
@@ -134,7 +142,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/line_layer.cpp b/src/mbgl/style/layers/line_layer.cpp
index 1c7f0d28ee..56eac34c00 100644
--- a/src/mbgl/style/layers/line_layer.cpp
+++ b/src/mbgl/style/layers/line_layer.cpp
@@ -82,12 +82,14 @@ void LineLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void LineLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/raster_layer.cpp b/src/mbgl/style/layers/raster_layer.cpp
index a9a8d273fa..36b2e3e027 100644
--- a/src/mbgl/style/layers/raster_layer.cpp
+++ b/src/mbgl/style/layers/raster_layer.cpp
@@ -59,12 +59,14 @@ void RasterLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void RasterLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 9a944657ca..c940f3b00a 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -82,12 +82,14 @@ void SymbolLayer::setMinZoom(float minZoom) {
auto impl_ = mutableImpl();
impl_->minZoom = minZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
void SymbolLayer::setMaxZoom(float maxZoom) {
auto impl_ = mutableImpl();
impl_->maxZoom = maxZoom;
baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
}
// Layout properties
@@ -412,15 +414,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 39e1c17722..0c7f924917 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..091840a371
--- /dev/null
+++ b/src/mbgl/text/collision_index.cpp
@@ -0,0 +1,339 @@
+#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, uint32_t bucketInstanceId) {
+ if (feature.alongLine) {
+ for (auto& circle : feature.boxes) {
+ if (!circle.used) {
+ continue;
+ }
+
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ circle.px, circle.py }, circle.radius});
+ }
+ }
+ } else {
+ assert(feature.boxes.size() == 1);
+ auto& box = feature.boxes[0];
+ if (ignorePlacement) {
+ ignoredGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ box.px1, box.py1 }, { box.px2, box.py2 }});
+ } else {
+ collisionGrid.insert(IndexedSubfeature(feature.indexedFeature, bucketInstanceId), {{ 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::unordered_map<uint32_t, std::vector<IndexedSubfeature>> CollisionIndex::queryRenderedSymbols(const ScreenLineString& queryGeometry) const {
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> result;
+ if (queryGeometry.empty() || (collisionGrid.empty() && ignoredGrid.empty())) {
+ return result;
+ }
+
+ LineString<float> gridQuery;
+ for (const auto& point : queryGeometry) {
+ gridQuery.emplace_back(point.x + viewportPadding, point.y + viewportPadding);
+ }
+
+ auto envelope = mapbox::geometry::envelope(gridQuery);
+
+ using QueryResult = std::pair<IndexedSubfeature, GridIndex<IndexedSubfeature>::BBox>;
+
+ std::vector<QueryResult> features = collisionGrid.queryWithBoxes(envelope);
+ std::vector<QueryResult> ignoredFeatures = ignoredGrid.queryWithBoxes(envelope);
+ features.insert(features.end(), ignoredFeatures.begin(), ignoredFeatures.end());
+
+ std::unordered_map<uint32_t, std::unordered_set<size_t>> seenBuckets;
+ for (auto& queryResult : features) {
+ auto& feature = queryResult.first;
+ auto& bbox = queryResult.second;
+
+ // Skip already seen features.
+ auto& seenFeatures = seenBuckets[feature.bucketInstanceId];
+ if (seenFeatures.find(feature.index) != seenFeatures.end())
+ continue;
+
+ if (!polygonIntersectsBox(gridQuery, bbox)) {
+ continue;
+ }
+
+ seenFeatures.insert(feature.index);
+ result[feature.bucketInstanceId].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..b2be4c6ade
--- /dev/null
+++ b/src/mbgl/text/collision_index.hpp
@@ -0,0 +1,69 @@
+#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, uint32_t bucketInstanceId);
+
+ std::unordered_map<uint32_t, std::vector<IndexedSubfeature>> queryRenderedSymbols(const ScreenLineString&) 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..b0c3511ce3
--- /dev/null
+++ b/src/mbgl/text/cross_tile_symbol_index.cpp
@@ -0,0 +1,193 @@
+#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) {
+ const 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.bucketLeaderID != symbolLayer.getID()) {
+ // Only add this layer if it's the "group leader" for the bucket
+ continue;
+ }
+
+ 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..9883a1f456
--- /dev/null
+++ b/src/mbgl/text/placement.cpp
@@ -0,0 +1,372 @@
+#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;
+ }
+ assert(dynamic_cast<GeometryTile*>(&renderTile.tile));
+ GeometryTile& geometryTile = static_cast<GeometryTile&>(renderTile.tile);
+
+
+ auto bucket = geometryTile.getBucket(*symbolLayer.baseImpl);
+ assert(dynamic_cast<SymbolBucket*>(bucket));
+ SymbolBucket& symbolBucket = *reinterpret_cast<SymbolBucket*>(bucket);
+
+ if (symbolBucket.bucketLeaderID != symbolLayer.getID()) {
+ // Only place this layer if it's the "group leader" for the bucket
+ continue;
+ }
+
+ auto& layout = symbolBucket.layout;
+
+ const float pixelsToTileUnits = renderTile.id.pixelsToTileUnits(1, state.getZoom());
+
+ const float scale = std::pow(2, state.getZoom() - geometryTile.id.overscaledZ);
+ const float textPixelRatio = (util::tileSize * geometryTile.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);
+
+
+ // As long as this placement lives, we have to hold onto this bucket's
+ // matching FeatureIndex/data for querying purposes
+ retainedQueryData.emplace(std::piecewise_construct,
+ std::forward_as_tuple(symbolBucket.bucketInstanceId),
+ std::forward_as_tuple(symbolBucket.bucketInstanceId, geometryTile.getFeatureIndex(), geometryTile.id));
+
+ 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());
+
+ 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;
+ }
+
+ const bool iconWithoutText = !symbolInstance.hasText || bucket.layout.get<style::TextOptional>();
+ const bool textWithoutIcon = !symbolInstance.hasIcon || bucket.layout.get<style::IconOptional>();
+
+ // combine placements for icon and text
+ if (!iconWithoutText && !textWithoutIcon) {
+ placeText = placeIcon = placeText && placeIcon;
+ } else if (!textWithoutIcon) {
+ placeText = placeText && placeIcon;
+ } else if (!iconWithoutText) {
+ placeIcon = placeText && placeIcon;
+ }
+
+ if (placeText) {
+ collisionIndex.insertFeature(symbolInstance.textCollisionFeature, bucket.layout.get<style::TextIgnorePlacement>(), bucket.bucketInstanceId);
+ }
+
+ if (placeIcon) {
+ collisionIndex.insertFeature(symbolInstance.iconCollisionFeature, bucket.layout.get<style::IconIgnorePlacement>(), bucket.bucketInstanceId);
+ }
+
+ 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);
+ if (symbolBucket.bucketLeaderID != symbolLayer.getID()) {
+ // Only update opacities this layer if it's the "group leader" for the bucket
+ continue;
+ }
+ 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());
+ auto retainedData = retainedQueryData.find(bucket.bucketInstanceId);
+ if (retainedData != retainedQueryData.end()) {
+ retainedData->second.featureSortOrder = bucket.featureSortOrder;
+ }
+}
+
+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;
+}
+
+const RetainedQueryData& Placement::getQueryData(uint32_t bucketInstanceId) const {
+ auto it = retainedQueryData.find(bucketInstanceId);
+ if (it == retainedQueryData.end()) {
+ throw std::runtime_error("Placement::getQueryData with unrecognized bucketInstanceId");
+ }
+ return it->second;
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/text/placement.hpp b/src/mbgl/text/placement.hpp
new file mode 100644
index 0000000000..0e1751b127
--- /dev/null
+++ b/src/mbgl/text/placement.hpp
@@ -0,0 +1,108 @@
+#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;
+};
+
+struct RetainedQueryData {
+ uint32_t bucketInstanceId;
+ std::shared_ptr<FeatureIndex> featureIndex;
+ OverscaledTileID tileID;
+ std::shared_ptr<std::vector<size_t>> featureSortOrder;
+
+ RetainedQueryData(uint32_t bucketInstanceId_,
+ std::shared_ptr<FeatureIndex> featureIndex_,
+ OverscaledTileID tileID_)
+ : bucketInstanceId(bucketInstanceId_)
+ , featureIndex(std::move(featureIndex_))
+ , tileID(std::move(tileID_)) {}
+};
+
+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();
+
+ const RetainedQueryData& getQueryData(uint32_t bucketInstanceId) const;
+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;
+
+ std::unordered_map<uint32_t, RetainedQueryData> retainedQueryData;
+};
+
+} // 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..a2fefcfa9f
--- /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)(style::expression::EvaluationContext { static_cast<float>(id.overscaledZ), feature.get() })) {
+ 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/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp
index bbec899950..f211c03569 100644
--- a/src/mbgl/tile/geojson_tile.cpp
+++ b/src/mbgl/tile/geojson_tile.cpp
@@ -30,7 +30,7 @@ void GeoJSONTile::querySourceFeatures(
auto feature = layer->getFeature(i);
// Apply filter, if any
- if (options.filter && !(*options.filter)(*feature)) {
+ if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast<float>(this->id.overscaledZ), feature.get() })) {
continue;
}
diff --git a/src/mbgl/tile/geometry_tile.cpp b/src/mbgl/tile/geometry_tile.cpp
index 8c018ce3aa..8efe12d54f 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,34 +116,32 @@ void GeometryTile::setLayers(const std::vector<Immutable<Layer::Impl>>& layers)
worker.invoke(&GeometryTileWorker::setLayers, std::move(impls), correlationID);
}
-void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
- loaded = true;
- renderable = true;
- (void)resultCorrelationID;
- nonSymbolBuckets = std::move(result.nonSymbolBuckets);
- featureIndex = std::move(result.featureIndex);
- data = std::move(result.tileData);
- collisionTile.reset();
- observer->onTileChanged(*this);
+void GeometryTile::setShowCollisionBoxes(const bool showCollisionBoxes_) {
+ if (showCollisionBoxes != showCollisionBoxes_) {
+ showCollisionBoxes = showCollisionBoxes_;
+ ++correlationID;
+ worker.invoke(&GeometryTileWorker::setShowCollisionBoxes, showCollisionBoxes, correlationID);
+ }
}
-void GeometryTile::onPlacement(PlacementResult result, const uint64_t resultCorrelationID) {
+void GeometryTile::onLayout(LayoutResult result, const uint64_t resultCorrelationID) {
loaded = true;
renderable = true;
if (resultCorrelationID == correlationID) {
pending = false;
}
- symbolBuckets = std::move(result.symbolBuckets);
- collisionTile = std::move(result.collisionTile);
+
+ buckets = std::move(result.buckets);
+
+ latestFeatureIndex = std::move(result.featureIndex);
+
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);
}
@@ -196,11 +176,7 @@ void GeometryTile::upload(gl::Context& context) {
}
};
- for (auto& entry : nonSymbolBuckets) {
- uploadFn(*entry.second);
- }
-
- for (auto& entry : symbolBuckets) {
+ for (auto& entry : buckets) {
uploadFn(*entry.second);
}
@@ -216,7 +192,6 @@ void GeometryTile::upload(gl::Context& context) {
}
Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const {
- const auto& buckets = layer.type == LayerType::Symbol ? symbolBuckets : nonSymbolBuckets;
const auto it = buckets.find(layer.id);
if (it == buckets.end()) {
return nullptr;
@@ -226,43 +201,51 @@ Bucket* GeometryTile::getBucket(const Layer::Impl& layer) const {
return it->second.get();
}
-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) {
-
- if (!featureIndex || !data) return;
-
- // Determine the additional radius needed factoring in property functions
- float additionalRadius = 0;
+float GeometryTile::getQueryPadding(const std::vector<const RenderLayer*>& layers) {
+ float queryPadding = 0;
for (const RenderLayer* layer : layers) {
auto bucket = getBucket(*layer->baseImpl);
- if (bucket) {
- additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(*layer));
+ if (bucket && bucket->hasData()) {
+ queryPadding = std::max(queryPadding, bucket->getQueryRadius(*layer));
}
}
+ return queryPadding;
+}
- featureIndex->query(result,
- queryGeometry,
- transformState.getAngle(),
- util::tileSize * id.overscaleFactor(),
- std::pow(2, transformState.getZoom() - id.overscaledZ),
- options,
- *data,
- id.canonical,
- layers,
- collisionTile.get(),
- additionalRadius);
+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 mat4& projMatrix) {
+
+ if (!getData()) return;
+
+ const float queryPadding = getQueryPadding(layers);
+
+ mat4 posMatrix;
+ transformState.matrixFor(posMatrix, id.toUnwrapped());
+ matrix::multiply(posMatrix, projMatrix, posMatrix);
+
+ latestFeatureIndex->query(result,
+ queryGeometry,
+ transformState,
+ posMatrix,
+ util::tileSize * id.overscaleFactor(),
+ std::pow(2, transformState.getZoom() - id.overscaledZ),
+ options,
+ id.toUnwrapped(),
+ layers,
+ queryPadding * transformState.maxPitchScaleFactor());
}
void GeometryTile::querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions& options) {
- // Data not yet available
- if (!data) {
+ // Data not yet available, or tile is empty
+ if (!getData()) {
return;
}
@@ -275,7 +258,7 @@ void GeometryTile::querySourceFeatures(
for (auto sourceLayer : *options.sourceLayers) {
// Go throught all sourceLayers, if any
// to gather all the features
- auto layer = data->getLayer(sourceLayer);
+ auto layer = getData()->getLayer(sourceLayer);
if (layer) {
auto featureCount = layer->featureCount();
@@ -283,7 +266,7 @@ void GeometryTile::querySourceFeatures(
auto feature = layer->getFeature(i);
// Apply filter, if any
- if (options.filter && !(*options.filter)(*feature)) {
+ if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast<float>(this->id.overscaledZ), feature.get() })) {
continue;
}
@@ -293,11 +276,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..af122474c2 100644
--- a/src/mbgl/tile/geometry_tile.hpp
+++ b/src/mbgl/tile/geometry_tile.hpp
@@ -4,10 +4,7 @@
#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>
#include <mbgl/geometry/feature_index.hpp>
@@ -36,9 +33,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,59 +53,51 @@ public:
const GeometryCoordinates& queryGeometry,
const TransformState&,
const std::vector<const RenderLayer*>& layers,
- const RenderedQueryOptions& options) override;
+ const RenderedQueryOptions& options,
+ const mat4& projMatrix) override;
void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&) override;
+ float getQueryPadding(const std::vector<const RenderLayer*>&) override;
+
void cancel() override;
class LayoutResult {
public:
- std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
std::unique_ptr<FeatureIndex> featureIndex;
- std::unique_ptr<GeometryTileData> tileData;
-
- LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> nonSymbolBuckets_,
- std::unique_ptr<FeatureIndex> featureIndex_,
- std::unique_ptr<GeometryTileData> tileData_)
- : nonSymbolBuckets(std::move(nonSymbolBuckets_)),
- featureIndex(std::move(featureIndex_)),
- tileData(std::move(tileData_)) {}
- };
- void onLayout(LayoutResult, uint64_t correlationID);
-
- 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_)),
+ LayoutResult(std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets_,
+ std::unique_ptr<FeatureIndex> featureIndex_,
+ optional<AlphaImage> glyphAtlasImage_,
+ optional<PremultipliedImage> iconAtlasImage_)
+ : buckets(std::move(buckets_)),
+ featureIndex(std::move(featureIndex_)),
glyphAtlasImage(std::move(glyphAtlasImage_)),
iconAtlasImage(std::move(iconAtlasImage_)) {}
};
- void onPlacement(PlacementResult, uint64_t correlationID);
+ void onLayout(LayoutResult, uint64_t correlationID);
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;
+
+ const std::shared_ptr<FeatureIndex> getFeatureIndex() const { return latestFeatureIndex; }
protected:
const GeometryTileData* getData() {
- return data.get();
+ return latestFeatureIndex ? latestFeatureIndex->getData() : nullptr;
}
private:
void markObsolete();
- void invokePlacement();
const std::string sourceID;
@@ -122,21 +111,26 @@ 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<const GeometryTileData> data;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
+
+ std::shared_ptr<FeatureIndex> latestFeatureIndex;
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..2e7d588d9b 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,15 +23,19 @@ 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;
@@ -42,32 +45,69 @@ GeometryTileWorker::~GeometryTileWorker() = default;
States are indicated by [state], lines are transitions triggered by
messages, (parentheses) are actions taken on transition.
- [idle] <----------------------------.
- | |
- set{Data,Layers,Placement}, symbolDependenciesChanged |
- | |
- (do layout/placement; self-send "coalesced") |
- v |
- [coalescing] --- coalesced ------------.
- | |
- .-----------------. .---------------.
+ [Idle] <-------------------------.
+ | |
+ set{Data,Layers}, symbolDependenciesChanged, |
+ setShowCollisionBoxes |
+ | |
+ (do parse and/or symbol layout; self-send "coalesced") |
+ v |
+ [Coalescing] --- coalesced ---------.
+ | |
+ .-----------. .---------------------.
| |
- .--- set{Data,Layers} setPlacement -----.
- | | | |
- | v v |
- .-- [need layout] <-- set{Data,Layers} -- [need placement] --.
+ .--- set{Data,Layers} setShowCollisionBoxes,
+ | | symbolDependenciesChanged --.
+ | | | |
+ | v v |
+ .-- [NeedsParse] <-- set{Data,Layers} -- [NeedsSymbolLayout] ---.
| |
coalesced coalesced
| |
v v
- (do layout or placement; self-send "coalesced"; goto [coalescing])
-
- The idea is that in the [idle] state, layout or placement happens immediately
- in response to a "set" message. During this processing, multiple "set" messages
- might get queued in the mailbox. At the end of processing, we self-send "coalesced",
- read all the queued messages until we get to "coalesced", and then redo either
- layout or placement if there were one or more "set"s (with layout taking priority,
- since it will trigger placement when complete), or return to the [idle] state if not.
+ (do parse or symbol layout; self-send "coalesced"; goto [coalescing])
+
+ The idea is that in the [idle] state, parsing happens immediately in response to
+ a "set" message, and symbol layout happens once all symbol dependencies are met.
+ During this processing, multiple "set" messages might get queued in the mailbox.
+ At the end of processing, we self-send "coalesced", read all the queued messages
+ until we get to "coalesced", and then re-parse if there were one or more "set"s or
+ return to the [idle] state if not.
+
+ One important goal of the design is to prevent starvation. Under heavy load new
+ requests for tiles should not prevent in progress request from completing.
+ It is nevertheless possible to restart an in-progress request:
+
+ - [Idle] setData -> parse()
+ sends getGlyphs, hasPendingSymbolDependencies() is true
+ enters [Coalescing], sends coalesced
+ - [Coalescing] coalesced -> [Idle]
+ - [Idle] setData -> new parse(), interrupts old parse()
+ sends getGlyphs, hasPendingSymbolDependencies() is true
+ enters [Coalescing], sends coalesced
+ - [Coalescing] onGlyphsAvailable -> [NeedsSymbolLayout]
+ hasPendingSymbolDependencies() may or may not be true
+ - [NeedsSymbolLayout] coalesced -> performSymbolLayout()
+ Generates result depending on whether dependencies are met
+ -> [Idle]
+
+ In this situation, we are counting on the idea that even with rapid changes to
+ the tile's data, the set of glyphs/images it requires will not keep growing without
+ limit.
+
+ Although parsing (which populates all non-symbol buckets and requests dependencies
+ for symbol buckets) is internally separate from symbol layout, we only return
+ results to the foreground when we have completed both steps. Because we _move_
+ the result buckets to the foreground, it is necessary to re-generate all buckets from
+ scratch for `setShowCollisionBoxes`, even though it only affects symbol layers.
+
+ The GL JS equivalent (in worker_tile.js and vector_tile_worker_source.js)
+ is somewhat simpler because it relies on getGlyphs/getImages calls that transfer
+ an entire set of glyphs/images on every tile load, while the native logic
+ maintains a local state that can be incrementally updated. Because each tile load
+ call becomes self-contained, the equivalent of the coalescing logic is handled by
+ 'reloadTile' queueing a single extra 'reloadTile' callback to run after the next
+ completed parse.
*/
void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_, uint64_t correlationID_) {
@@ -77,14 +117,14 @@ void GeometryTileWorker::setData(std::unique_ptr<const GeometryTileData> data_,
switch (state) {
case Idle:
- redoLayout();
+ parse();
coalesce();
break;
case Coalescing:
- case NeedLayout:
- case NeedPlacement:
- state = NeedLayout;
+ case NeedsParse:
+ case NeedsSymbolLayout:
+ state = NeedsParse;
break;
}
} catch (...) {
@@ -99,16 +139,16 @@ void GeometryTileWorker::setLayers(std::vector<Immutable<Layer::Impl>> layers_,
switch (state) {
case Idle:
- redoLayout();
+ parse();
coalesce();
break;
case Coalescing:
- case NeedPlacement:
- state = NeedLayout;
+ case NeedsSymbolLayout:
+ state = NeedsParse;
break;
- case NeedLayout:
+ case NeedsParse:
break;
}
} catch (...) {
@@ -116,23 +156,27 @@ 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) {
case Idle:
- attemptPlacement();
- coalesce();
+ if (!hasPendingParseResult()) {
+ // Trigger parse if nothing is in flight, otherwise symbol layout will automatically
+ // pick up the change
+ parse();
+ coalesce();
+ }
break;
case Coalescing:
- state = NeedPlacement;
+ state = NeedsSymbolLayout;
break;
- case NeedPlacement:
- case NeedLayout:
+ case NeedsSymbolLayout:
+ case NeedsParse:
break;
}
} catch (...) {
@@ -145,19 +189,23 @@ void GeometryTileWorker::symbolDependenciesChanged() {
switch (state) {
case Idle:
if (symbolLayoutsNeedPreparation) {
- attemptPlacement();
+ // symbolLayoutsNeedPreparation can only be set true by parsing
+ // and the parse result can only be cleared by performSymbolLayout
+ // which also clears symbolLayoutsNeedPreparation
+ assert(hasPendingParseResult());
+ performSymbolLayout();
coalesce();
}
break;
case Coalescing:
if (symbolLayoutsNeedPreparation) {
- state = NeedPlacement;
+ state = NeedsSymbolLayout;
}
break;
- case NeedPlacement:
- case NeedLayout:
+ case NeedsSymbolLayout:
+ case NeedsParse:
break;
}
} catch (...) {
@@ -176,13 +224,16 @@ void GeometryTileWorker::coalesced() {
state = Idle;
break;
- case NeedLayout:
- redoLayout();
+ case NeedsParse:
+ parse();
coalesce();
break;
- case NeedPlacement:
- attemptPlacement();
+ case NeedsSymbolLayout:
+ // We may have entered NeedsSymbolLayout while coalescing
+ // after a performSymbolLayout. In that case, we need to
+ // start over with parsing in order to do another layout.
+ hasPendingParseResult() ? performSymbolLayout() : parse();
coalesce();
break;
}
@@ -264,7 +315,7 @@ static std::vector<std::unique_ptr<RenderLayer>> toRenderLayers(const std::vecto
return renderLayers;
}
-void GeometryTileWorker::redoLayout() {
+void GeometryTileWorker::parse() {
if (!data || !layers) {
return;
}
@@ -277,8 +328,8 @@ void GeometryTileWorker::redoLayout() {
}
std::unordered_map<std::string, std::unique_ptr<SymbolLayout>> symbolLayoutMap;
- std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
- auto featureIndex = std::make_unique<FeatureIndex>();
+ buckets.clear();
+ featureIndex = std::make_unique<FeatureIndex>(*data ? (*data)->clone() : nullptr);
BucketParameters parameters { id, mode, pixelRatio };
GlyphDependencies glyphDependencies;
@@ -324,7 +375,7 @@ void GeometryTileWorker::redoLayout() {
for (std::size_t i = 0; !obsolete && i < geometryLayer->featureCount(); i++) {
std::unique_ptr<GeometryTileFeature> feature = geometryLayer->getFeature(i);
- if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
+ if (!filter(expression::EvaluationContext { static_cast<float>(this->id.overscaledZ), feature.get() }))
continue;
GeometryCollection geometries = feature->getGeometries();
@@ -353,13 +404,7 @@ void GeometryTileWorker::redoLayout() {
requestNewGlyphs(glyphDependencies);
requestNewImages(imageDependencies);
- parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
- std::move(buckets),
- std::move(featureIndex),
- *data ? (*data)->clone() : nullptr,
- }, correlationID);
-
- attemptPlacement();
+ performSymbolLayout();
}
bool GeometryTileWorker::hasPendingSymbolDependencies() const {
@@ -370,9 +415,13 @@ bool GeometryTileWorker::hasPendingSymbolDependencies() const {
}
return !pendingImageDependencies.empty();
}
+
+bool GeometryTileWorker::hasPendingParseResult() const {
+ return bool(featureIndex);
+}
-void GeometryTileWorker::attemptPlacement() {
- if (!data || !layers || !placementConfig || hasPendingSymbolDependencies()) {
+void GeometryTileWorker::performSymbolLayout() {
+ if (!data || !layers || !hasPendingParseResult() || hasPendingSymbolDependencies()) {
return;
}
@@ -398,9 +447,6 @@ void GeometryTileWorker::attemptPlacement() {
symbolLayoutsNeedPreparation = false;
}
- auto collisionTile = std::make_unique<CollisionTile>(*placementConfig);
- std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
-
for (auto& symbolLayout : symbolLayouts) {
if (obsolete) {
return;
@@ -410,17 +456,22 @@ 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);
}
}
- parent.invoke(&GeometryTile::onPlacement, GeometryTile::PlacementResult {
+ firstLoad = false;
+
+ parent.invoke(&GeometryTile::onLayout, GeometryTile::LayoutResult {
std::move(buckets),
- std::move(collisionTile),
+ std::move(featureIndex),
std::move(glyphAtlasImage),
- std::move(iconAtlasImage),
+ std::move(iconAtlasImage)
}, correlationID);
}
diff --git a/src/mbgl/tile/geometry_tile_worker.hpp b/src/mbgl/tile/geometry_tile_worker.hpp
index 1425daa7a1..b5417c8114 100644
--- a/src/mbgl/tile/geometry_tile_worker.hpp
+++ b/src/mbgl/tile/geometry_tile_worker.hpp
@@ -4,11 +4,12 @@
#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>
#include <mbgl/style/layer_impl.hpp>
+#include <mbgl/geometry/feature_index.hpp>
+#include <mbgl/renderer/bucket.hpp>
#include <atomic>
#include <memory>
@@ -28,22 +29,24 @@ 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);
private:
void coalesced();
- void redoLayout();
- void attemptPlacement();
+ void parse();
+ void performSymbolLayout();
void coalesce();
@@ -52,20 +55,25 @@ private:
void symbolDependenciesChanged();
bool hasPendingSymbolDependencies() const;
+ bool hasPendingParseResult() const;
ActorRef<GeometryTileWorker> self;
ActorRef<GeometryTile> parent;
const OverscaledTileID id;
+ const std::string sourceID;
const std::atomic<bool>& obsolete;
const MapMode mode;
const float pixelRatio;
+
+ std::unique_ptr<FeatureIndex> featureIndex;
+ std::unordered_map<std::string, std::shared_ptr<Bucket>> buckets;
enum State {
Idle,
Coalescing,
- NeedLayout,
- NeedPlacement
+ NeedsParse,
+ NeedsSymbolLayout
};
State state = Idle;
@@ -75,7 +83,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 +90,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..b95944f10e 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,12 @@ void Tile::queryRenderedFeatures(
const GeometryCoordinates&,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions&) {}
+ const RenderedQueryOptions&,
+ const mat4&) {}
+
+float Tile::getQueryPadding(const std::vector<const RenderLayer*>&) {
+ return 0;
+}
void Tile::querySourceFeatures(
std::vector<Feature>&,
diff --git a/src/mbgl/tile/tile.hpp b/src/mbgl/tile/tile.hpp
index 8be7c4d862..23d6864205 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,12 +57,15 @@ public:
const GeometryCoordinates& queryGeometry,
const TransformState&,
const std::vector<const RenderLayer*>&,
- const RenderedQueryOptions& options);
+ const RenderedQueryOptions& options,
+ const mat4& projMatrix);
virtual void querySourceFeatures(
std::vector<Feature>& result,
const SourceQueryOptions&);
+ virtual float getQueryPadding(const std::vector<const RenderLayer*>&);
+
void setTriedCache();
// Returns true when the tile source has received a first response, regardless of whether a load
@@ -92,7 +96,21 @@ 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() {}
+
void dumpDebugLogs() const;
const OverscaledTileID id;
@@ -101,8 +119,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..55f1b65436 100644
--- a/src/mbgl/util/color.cpp
+++ b/src/mbgl/util/color.cpp
@@ -23,11 +23,25 @@ optional<Color> Color::parse(const std::string& s) {
}
std::string Color::stringify() const {
+ std::array<double, 4> array = toArray();
return "rgba(" +
- util::toString(r * 255) + "," +
- util::toString(g * 255) + "," +
- util::toString(b * 255) + "," +
- util::toString(a) + ")";
+ util::toString(array[0]) + "," +
+ util::toString(array[1]) + "," +
+ util::toString(array[2]) + "," +
+ util::toString(array[3]) + ")";
+}
+
+std::array<double, 4> Color::toArray() const {
+ if (a == 0) {
+ return {{ 0, 0, 0, 0 }};
+ } else {
+ return {{
+ r * 255 / a,
+ g * 255 / a,
+ b * 255 / a,
+ a,
+ }};
+ }
}
} // namespace mbgl
diff --git a/src/mbgl/util/compression.cpp b/src/mbgl/util/compression.cpp
index 94089c1b26..ee3ebe7cae 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) && !defined(__GNUC__)
+#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/i18n.cpp b/src/mbgl/util/i18n.cpp
index 1fc13bfb7d..5530796915 100644
--- a/src/mbgl/util/i18n.cpp
+++ b/src/mbgl/util/i18n.cpp
@@ -1,4 +1,5 @@
-#include "i18n.hpp"
+#include <mbgl/util/i18n.hpp>
+#include <mbgl/util/utf.hpp>
#include <algorithm>
#include <map>
@@ -65,7 +66,7 @@ DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabics, 0x1400, 0x167F)
// DEFINE_IS_IN_UNICODE_BLOCK(Hanunoo, 0x1720, 0x173F)
// DEFINE_IS_IN_UNICODE_BLOCK(Buhid, 0x1740, 0x175F)
// DEFINE_IS_IN_UNICODE_BLOCK(Tagbanwa, 0x1760, 0x177F)
-// DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF)
+DEFINE_IS_IN_UNICODE_BLOCK(Khmer, 0x1780, 0x17FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Mongolian, 0x1800, 0x18AF)
DEFINE_IS_IN_UNICODE_BLOCK(UnifiedCanadianAboriginalSyllabicsExtended, 0x18B0, 0x18FF)
// DEFINE_IS_IN_UNICODE_BLOCK(Limbu, 0x1900, 0x194F)
@@ -581,6 +582,38 @@ std::u16string verticalizePunctuation(const std::u16string& input) {
char16_t verticalizePunctuation(char16_t chr) {
return verticalPunctuation.count(chr) ? verticalPunctuation.at(chr) : 0;
}
+
+bool charInSupportedScript(char16_t chr) {
+ // This is a rough heuristic: whether we "can render" a script
+ // actually depends on the properties of the font being used
+ // and whether differences from the ideal rendering are considered
+ // semantically significant.
+
+ // Even in Latin script, we "can't render" combinations such as the fi
+ // ligature, but we don't consider that semantically significant.n false;
+ if ((chr >= 0x0900 && chr <= 0x0DFF) ||
+ // Main blocks for Indic scripts and Sinhala
+ (chr >= 0x0F00 && chr <= 0x109F) ||
+ // Main blocks for Tibetan and Myanmar
+ isInKhmer(chr)) {
+ // These blocks cover common scripts that require
+ // complex text shaping, based on unicode script metadata:
+ // http://www.unicode.org/repos/cldr/trunk/common/properties/scriptMetadata.txt
+ // where "Web Rank <= 32" "Shaping Required = YES"
+ return false;
+ }
+ return true;
+}
+
+bool isStringInSupportedScript(const std::string& input) {
+ auto u16string = util::utf8_to_utf16::convert(input);
+ for (char16_t chr : u16string) {
+ if (!charInSupportedScript(chr)) {
+ return false;
+ }
+ }
+ return true;
+}
} // namespace i18n
} // namespace util
diff --git a/src/mbgl/util/i18n.hpp b/src/mbgl/util/i18n.hpp
index b3960c743c..a74215a134 100644
--- a/src/mbgl/util/i18n.hpp
+++ b/src/mbgl/util/i18n.hpp
@@ -72,6 +72,8 @@ std::u16string verticalizePunctuation(const std::u16string& input);
@return The character’s specialized vertical form; 0 if not applicable. */
char16_t verticalizePunctuation(char16_t chr);
+
+bool isStringInSupportedScript(const std::string& input);
} // namespace i18n
} // namespace util
diff --git a/src/mbgl/util/intersection_tests.cpp b/src/mbgl/util/intersection_tests.cpp
index e6ce245c0e..780fce98f9 100644
--- a/src/mbgl/util/intersection_tests.cpp
+++ b/src/mbgl/util/intersection_tests.cpp
@@ -82,11 +82,16 @@ bool lineIntersectsBufferedLine(const GeometryCoordinates& lineA, const Geometry
return false;
}
+bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius) {
+ if (polygonContainsPoint(polygon, point)) return true;
+ if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
+ return false;
+}
+
bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates& polygon, const GeometryCollection& rings, float radius) {
for (auto& ring : rings) {
for (auto& point : ring) {
- if (polygonContainsPoint(polygon, point)) return true;
- if (pointIntersectsBufferedLine(point, polygon, radius)) return true;
+ if (polygonIntersectsBufferedPoint(polygon, point, radius)) return true;
}
}
return false;
diff --git a/src/mbgl/util/intersection_tests.hpp b/src/mbgl/util/intersection_tests.hpp
index 5bcb29c767..c105fe4dd0 100644
--- a/src/mbgl/util/intersection_tests.hpp
+++ b/src/mbgl/util/intersection_tests.hpp
@@ -9,6 +9,7 @@ bool polygonIntersectsBufferedMultiPoint(const GeometryCoordinates&, const Geome
bool polygonIntersectsBufferedMultiLine(const GeometryCoordinates&, const GeometryCollection&, float radius);
bool polygonIntersectsPolygon(const GeometryCoordinates&, const GeometryCoordinates&);
bool polygonIntersectsMultiPolygon(const GeometryCoordinates&, const GeometryCollection&);
+bool polygonIntersectsBufferedPoint(const GeometryCoordinates& polygon, const GeometryCoordinate& point, float radius);
} // namespace util
} // 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/throttler.cpp b/src/mbgl/util/throttler.cpp
deleted file mode 100644
index 910810ce2f..0000000000
--- a/src/mbgl/util/throttler.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include <mbgl/util/throttler.hpp>
-
-namespace mbgl {
-namespace util {
-
-Throttler::Throttler(Duration frequency_, std::function<void()>&& function_)
- : frequency(frequency_)
- , function(std::move(function_))
- , pendingInvocation(false)
- , lastInvocation(TimePoint::min())
-{}
-
-void Throttler::invoke() {
- if (pendingInvocation) {
- return;
- }
-
- Duration timeToNextInvocation = lastInvocation == TimePoint::min()
- ? Duration::zero()
- : (lastInvocation + frequency) - Clock::now();
-
- if (timeToNextInvocation <= Duration::zero()) {
- lastInvocation = Clock::now();
- function();
- } else {
- pendingInvocation = true;
- timer.start(timeToNextInvocation, Duration::zero(), [this]{
- pendingInvocation = false;
- lastInvocation = Clock::now();
- function();
- });
- }
-}
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/throttler.hpp b/src/mbgl/util/throttler.hpp
deleted file mode 100644
index 175de7ccaf..0000000000
--- a/src/mbgl/util/throttler.hpp
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <mbgl/util/chrono.hpp>
-#include <mbgl/util/timer.hpp>
-
-namespace mbgl {
-namespace util {
-
-class Throttler {
-public:
- Throttler(Duration frequency, std::function<void()>&& function);
-
- void invoke();
-private:
- Duration frequency;
- std::function<void()> function;
-
- Timer timer;
- bool pendingInvocation;
- TimePoint lastInvocation;
-};
-
-} // namespace util
-} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover.cpp b/src/mbgl/util/tile_cover.cpp
index a5a1b1d70c..488e6b88ce 100644
--- a/src/mbgl/util/tile_cover.cpp
+++ b/src/mbgl/util/tile_cover.cpp
@@ -2,13 +2,18 @@
#include <mbgl/util/constants.hpp>
#include <mbgl/util/interpolate.hpp>
#include <mbgl/map/transform_state.hpp>
+#include <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
#include <functional>
+#include <list>
namespace mbgl {
namespace {
+using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>;
+
// Taken from polymaps src/Layer.js
// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
struct edge {
@@ -27,8 +32,6 @@ struct edge {
}
};
-using ScanLine = const std::function<void(int32_t x0, int32_t x1, int32_t y)>;
-
// scan-line conversion
static void scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
double y0 = ::fmax(ymin, std::floor(e1.y0));
@@ -126,9 +129,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);
@@ -147,11 +150,11 @@ std::vector<UnwrappedTileID> tileCover(const LatLngBounds& bounds_, int32_t z) {
{ std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });
return tileCover(
- TileCoordinate::fromLatLng(z, bounds.northwest()).p,
- TileCoordinate::fromLatLng(z, bounds.northeast()).p,
- TileCoordinate::fromLatLng(z, bounds.southeast()).p,
- TileCoordinate::fromLatLng(z, bounds.southwest()).p,
- TileCoordinate::fromLatLng(z, bounds.center()).p,
+ Projection::project(bounds.northwest(), z),
+ Projection::project(bounds.northeast(), z),
+ Projection::project(bounds.southeast(), z),
+ Projection::project(bounds.southwest(), z),
+ Projection::project(bounds.center(), z),
z);
}
@@ -169,25 +172,80 @@ std::vector<UnwrappedTileID> tileCover(const TransformState& state, int32_t z) {
z);
}
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>& geometry, int32_t z) {
+ std::vector<UnwrappedTileID> result;
+ TileCover tc(geometry, z, true);
+ while (tc.hasNext()) {
+ result.push_back(*tc.next());
+ };
+
+ return result;
+}
+
// Taken from https://github.com/mapbox/sphericalmercator#xyzbbox-zoom-tms_style-srs
// Computes the projected tiles for the lower left and upper right points of the bounds
// and uses that to compute the tile cover count
-uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom, uint16_t tileSize_){
-
- auto sw = Projection::project(bounds.southwest().wrapped(), zoom, tileSize_);
- auto ne = Projection::project(bounds.northeast().wrapped(), zoom, tileSize_);
-
- auto x1 = floor(sw.x/ tileSize_);
- auto x2 = floor((ne.x - 1) / tileSize_);
- auto y1 = floor(sw.y/ tileSize_);
- auto y2 = floor((ne.y - 1) / tileSize_);
-
- auto minX = std::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);
-
- return (maxX - minX + 1) * (maxY - minY + 1);
+uint64_t tileCount(const LatLngBounds& bounds, uint8_t zoom){
+ if (zoom == 0) {
+ return 1;
+ }
+ auto sw = Projection::project(bounds.southwest(), zoom);
+ auto ne = Projection::project(bounds.northeast(), zoom);
+ auto maxTile = std::pow(2.0, zoom);
+ auto x1 = floor(sw.x);
+ auto x2 = ceil(ne.x) - 1;
+ auto y1 = util::clamp(floor(sw.y), 0.0, maxTile - 1);
+ auto y2 = util::clamp(floor(ne.y), 0.0, maxTile - 1);
+
+ auto dx = x1 > x2 ? (maxTile - x1) + x2 : x2 - x1;
+ auto dy = y1 - y2;
+ return (dx + 1) * (dy + 1);
+}
+
+uint64_t tileCount(const Geometry<double>& geometry, uint8_t z) {
+ uint64_t tileCount = 0;
+
+ TileCover tc(geometry, z, true);
+ while (tc.next()) {
+ tileCount++;
+ };
+ return tileCount;
+}
+
+TileCover::TileCover(const LatLngBounds&bounds_, int32_t z) {
+ LatLngBounds bounds = LatLngBounds::hull(
+ { std::max(bounds_.south(), -util::LATITUDE_MAX), bounds_.west() },
+ { std::min(bounds_.north(), util::LATITUDE_MAX), bounds_.east() });
+
+ if (bounds.isEmpty() ||
+ bounds.south() > util::LATITUDE_MAX ||
+ bounds.north() < -util::LATITUDE_MAX) {
+ bounds = LatLngBounds::world();
+ }
+
+ auto sw = Projection::project(bounds.southwest(), z);
+ auto ne = Projection::project(bounds.northeast(), z);
+ auto se = Projection::project(bounds.southeast(), z);
+ auto nw = Projection::project(bounds.northwest(), z);
+
+ Polygon<double> p({ {sw, nw, ne, se, sw} });
+ impl = std::make_unique<TileCover::Impl>(z, p, false);
+}
+
+TileCover::TileCover(const Geometry<double>& geom, int32_t z, bool project/* = true*/)
+ : impl( std::make_unique<TileCover::Impl>(z, geom, project)) {
+}
+
+TileCover::~TileCover() {
+
+}
+
+optional<UnwrappedTileID> TileCover::next() {
+ return impl->next();
+}
+
+bool TileCover::hasNext() {
+ return impl->hasNext();
}
} // namespace util
diff --git a/src/mbgl/util/tile_cover.hpp b/src/mbgl/util/tile_cover.hpp
index 3c7a4ee44a..c953d764d2 100644
--- a/src/mbgl/util/tile_cover.hpp
+++ b/src/mbgl/util/tile_cover.hpp
@@ -2,9 +2,11 @@
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/style/types.hpp>
-#include <mbgl/util/tile_coordinate.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
#include <vector>
+#include <memory>
namespace mbgl {
@@ -13,13 +15,31 @@ class LatLngBounds;
namespace util {
-int32_t coveringZoomLevel(double z, SourceType type, uint16_t tileSize);
+// Helper class to stream tile-cover results per row
+class TileCover {
+public:
+ TileCover(const LatLngBounds&, int32_t z);
+ // When project == true, projects the geometry points to tile coordinates
+ TileCover(const Geometry<double>&, int32_t z, bool project = true);
+ ~TileCover();
+
+ optional<UnwrappedTileID> next();
+ bool hasNext();
+
+private:
+ class Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+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);
+std::vector<UnwrappedTileID> tileCover(const Geometry<double>&, int32_t z);
// Compute only the count of tiles needed for tileCover
-uint64_t tileCount(const LatLngBounds&, uint8_t z, uint16_t tileSize);
+uint64_t tileCount(const LatLngBounds&, uint8_t z);
+uint64_t tileCount(const Geometry<double>&, uint8_t z);
} // namespace util
} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover_impl.cpp b/src/mbgl/util/tile_cover_impl.cpp
new file mode 100644
index 0000000000..b3fc07f7dd
--- /dev/null
+++ b/src/mbgl/util/tile_cover_impl.cpp
@@ -0,0 +1,365 @@
+#include <mbgl/util/tile_cover_impl.hpp>
+#include <mbgl/util/tile_coordinate.hpp>
+
+#include <functional>
+#include <cmath>
+#include <assert.h>
+#include <limits.h>
+#include <algorithm>
+
+namespace mbgl {
+namespace util {
+
+using PointList = std::vector<Point<double>>;
+
+struct TileSpan {
+ int32_t xmin, xmax;
+ bool winding;
+};
+
+
+// Find the first local minimum going forward in the list.
+void start_list_on_local_minimum(PointList& points) {
+ auto prev_pt = std::prev(points.end(), 2);
+ auto pt = points.begin();
+ auto next_pt = std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y <= prev_pt->y) &&
+ (pt->y < next_pt->y)) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+ //Re-close linear rings with first_pt = last_pt
+ if (points.back() == points.front()) {
+ points.pop_back();
+ }
+ std::rotate(points.begin(), pt, points.end());
+ points.push_back(*points.begin());
+}
+
+//Create a bound towards a local maximum point, starting from pt.
+Bound create_bound_towards_maximum(PointList& points, PointList::iterator& pt) {
+ if (std::distance(pt, points.end()) < 2) { return {}; }
+ if (std::distance(pt, points.end()) == 2) {
+ Bound bnd;
+ if (pt->y < std::next(pt)->y) {
+ std::copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = true;
+ }
+ else {
+ std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = false;
+ }
+ pt = points.end();
+ return bnd;
+ }
+ const auto begin = pt;
+ auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
+ auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y >= prev_pt->y) &&
+ (pt->y > next_pt->y )) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+
+ Bound bnd;
+ if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
+ bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
+ std::copy(begin, next_pt, std::back_inserter(bnd.points));
+ bnd.winding = true;
+ return bnd;
+}
+
+//Create a bound towards a local minimum point, starting from pt.
+Bound create_bound_towards_minimum(PointList& points, PointList::iterator& pt) {
+ if (std::distance(pt, points.end()) < 2) { return {}; }
+ if (std::distance(pt, points.end()) == 2) {
+ Bound bnd;
+ if (pt->y < std::next(pt)->y) {
+ std::copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = true;
+ }
+ else {
+ std::reverse_copy(pt, points.end(), std::back_inserter(bnd.points));
+ bnd.winding = false;
+ }
+ pt = points.end();
+ return bnd;
+ }
+ auto begin = pt;
+ auto prev_pt = pt == points.begin() ? std::prev(points.end(), 2) : std::prev(pt);
+ auto next_pt = std::next(pt) == points.end() ? std::next(points.begin()) : std::next(pt);
+ while (pt != points.end()) {
+ if ((pt->y <= prev_pt->y) &&
+ (pt->y < next_pt->y)) {
+ break;
+ }
+ prev_pt = pt;
+ pt++;
+ next_pt++;
+ if (next_pt == points.end()) { next_pt = std::next(points.begin()); }
+ }
+
+ Bound bnd;
+ if (std::next(pt) == points.end()) { next_pt = points.end(); pt++; };
+ bnd.points.reserve(static_cast<std::size_t>(std::distance(begin, next_pt)));
+ //For bounds that start at a max, reverse copy so that all bounds start at a min
+ std::reverse_copy(begin, next_pt, std::back_inserter(bnd.points));
+ bnd.winding = false;
+ return bnd;
+}
+
+//Build a map of bounds and their starting Y tile coordinate.
+void build_bounds_map(PointList& points, uint32_t maxTile, BoundsMap& et, bool closed = false) {
+ if (points.size() < 2) return;
+ //While traversing closed rings, start the bounds at a local minimum
+ if (closed) {
+ start_list_on_local_minimum(points);
+ }
+
+ auto pointsIter = points.begin();
+ while (pointsIter != points.end()) {
+ Bound to_max = create_bound_towards_maximum(points, pointsIter);
+ Bound to_min = create_bound_towards_minimum(points, pointsIter);
+
+ if (to_max.points.size() > 0) {
+ // Projections may result in values beyond the bounds, clamp to max tile coordinates
+ const auto y = static_cast<uint32_t>(std::floor(clamp(to_max.points.front().y, 0.0, (double)maxTile)));
+ et[y].push_back(to_max);
+ }
+ if (to_min.points.size() > 0) {
+ const auto y = static_cast<uint32_t>(std::floor(clamp(to_min.points.front().y, 0.0, (double)maxTile)));
+ et[y].push_back(to_min);
+ }
+ }
+ assert(pointsIter == points.end());
+}
+
+void update_span(TileSpan& xp, double x) {
+ xp.xmin = std::min(xp.xmin, static_cast<int32_t>(std::floor(x)));
+ xp.xmax = std::max(xp.xmax, static_cast<int32_t>(std::ceil(x)));
+}
+
+//Build a vector of X tile-coordinates spanned by each bound.
+std::vector<TileSpan> scan_row(uint32_t y, Bounds& aet) {
+ std::vector<TileSpan> tile_range;
+ tile_range.reserve(aet.size());
+
+ for(Bound& b: aet) {
+ TileSpan xp = { INT_MAX, 0, b.winding };
+ double x;
+ const auto numEdges = b.points.size() - 1;
+ assert(numEdges >= 1);
+ while (b.currentPoint < numEdges) {
+ x = b.interpolate(y);
+ update_span(xp, x);
+
+ // If this edge ends beyond the current row, find the x-intercept where
+ // it exits the row
+ auto& p1 = b.points[b.currentPoint + 1];
+ if (p1.y > y+1) {
+ x = b.interpolate(y+1);
+ update_span(xp, x);
+ break;
+ } else if(b.currentPoint == numEdges - 1) {
+ // For last edge, consider x-intercept at the end of the edge.
+ x = p1.x;
+ update_span(xp, x);
+ }
+ b.currentPoint++;
+ }
+ tile_range.push_back(xp);
+ }
+ // Erase bounds in the active table whose current edge ends inside this row,
+ // or there are no more edges
+ auto bound = aet.begin();
+ while (bound != aet.end()) {
+ if ( bound->currentPoint == bound->points.size() - 1 &&
+ bound->points[bound->currentPoint].y <= y+1) {
+ bound = aet.erase(bound);
+ } else {
+ bound++;
+ }
+ }
+ // Sort the X-extents of each crossing bound by x_min, x_max
+ std::sort(tile_range.begin(), tile_range.end(), [] (TileSpan& a, TileSpan& b) {
+ return std::tie(a.xmin, a.xmax) < std::tie(b.xmin, b.xmax);
+ });
+
+ return tile_range;
+}
+
+struct BuildBoundsMap {
+ int32_t zoom;
+ bool project = false;
+ BuildBoundsMap(int32_t z, bool p): zoom(z), project(p) {}
+
+ void buildTable(const std::vector<Point<double>>& points, BoundsMap& et, bool closed = false) const {
+ PointList projectedPoints;
+ if (project) {
+ projectedPoints.reserve(points.size());
+ for(const auto&p : points) {
+ projectedPoints.push_back(
+ Projection::project(LatLng{ p.y, p.x }, zoom));
+ }
+ } else {
+ projectedPoints.insert(projectedPoints.end(), points.begin(), points.end());
+ }
+ build_bounds_map(projectedPoints, 1 << zoom, et, closed);
+ }
+
+ void buildPolygonTable(const Polygon<double>& polygon, BoundsMap& et) const {
+ for(const auto&ring : polygon) {
+ buildTable(ring, et, true);
+ }
+ }
+ BoundsMap operator()(const Point<double>&p) const {
+ Bound bnd;
+ auto point = p;
+ if(project) {
+ point = Projection::project(LatLng{p.y, p.x}, zoom);
+ }
+ bnd.points.insert(bnd.points.end(), 2, point);
+ bnd.winding = false;
+ BoundsMap et;
+ const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPoint<double>& points) const {
+ BoundsMap et;
+ for (const Point<double>& p: points) {
+ Bound bnd;
+ auto point = p;
+ if(project) {
+ point = Projection::project(LatLng{p.y, p.x}, zoom);
+ }
+ bnd.points.insert(bnd.points.end(), 2, point);
+ bnd.winding = false;
+ const auto y = static_cast<uint32_t>(std::floor(clamp(point.y, 0.0, (double)(1 << zoom))));
+ et[y].push_back(bnd);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const LineString<double>& lines) const {
+ BoundsMap et;
+ buildTable(lines, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiLineString<double>& lines) const {
+ BoundsMap et;
+ for(const auto&line : lines) {
+ buildTable(line, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const Polygon<double>& polygon) const {
+ BoundsMap et;
+ buildPolygonTable(polygon, et);
+ return et;
+ }
+
+ BoundsMap operator()(const MultiPolygon<double>& polygons) const {
+ BoundsMap et;
+ for(const auto& polygon: polygons) {
+ buildPolygonTable(polygon, et);
+ }
+ return et;
+ }
+
+ BoundsMap operator()(const mapbox::geometry::geometry_collection<double>&) const {
+ return {};
+ }
+};
+
+TileCover::Impl::Impl(int32_t z, const Geometry<double>& geom, bool project)
+ : zoom(z) {
+ ToFeatureType toFeatureType;
+ isClosed = apply_visitor(toFeatureType, geom) == FeatureType::Polygon;
+
+ BuildBoundsMap toBoundsMap(z, project);
+ boundsMap = apply_visitor(toBoundsMap, geom);
+ if (boundsMap.size() == 0) return;
+
+ //Iniitalize the active edge table, and current row span
+ currentBounds = boundsMap.begin();
+ tileY = 0;
+ nextRow();
+ if (tileXSpans.empty()) return;
+ tileX = tileXSpans.front().first;
+}
+
+void TileCover::Impl::nextRow() {
+ // Update AET for next row
+ if (currentBounds != boundsMap.end()) {
+ if (activeBounds.size() == 0 && currentBounds->first > tileY) {
+ //For multi-geoms: use the next row with an edge table starting point
+ tileY = currentBounds->first;
+ }
+ if (tileY == currentBounds->first) {
+
+ std::move(currentBounds->second.begin(), currentBounds->second.end(), std::back_inserter(activeBounds));
+ currentBounds++;
+ }
+ }
+ //Scan aet and update currenRange with x_min, x_max pairs
+ auto xps = util::scan_row(tileY, activeBounds);
+ if (xps.size() == 0) {
+ return;
+ }
+
+ auto x_min = xps[0].xmin;
+ auto x_max = xps[0].xmax;
+ int32_t nzRule = xps[0].winding ? 1 : -1;
+ for (size_t i = 1; i < xps.size(); i++) {
+ auto xp = xps[i];
+ if (!(isClosed && nzRule != 0)) {
+ if (xp.xmin > x_max && xp.xmax >= x_max) {
+ tileXSpans.emplace(x_min, x_max);
+ x_min = xp.xmin;
+ }
+ }
+ nzRule += xp.winding ? 1 : -1;
+ x_max = std::max(x_min, xp.xmax);
+ }
+ tileXSpans.emplace(x_min, x_max);
+}
+
+bool TileCover::Impl::hasNext() const {
+ return (!tileXSpans.empty() && tileX < tileXSpans.front().second && tileY < (1u << zoom));
+}
+
+optional<UnwrappedTileID> TileCover::Impl::next() {
+ if (!hasNext()) return {};
+
+ const auto x = tileX;
+ const auto y = tileY;
+ tileX++;
+ if (tileX >= tileXSpans.front().second) {
+ tileXSpans.pop();
+ if(tileXSpans.empty()) {
+ tileY++;
+ nextRow();
+ }
+ if (!tileXSpans.empty()) {
+ tileX = tileXSpans.front().first;
+ }
+ }
+ return UnwrappedTileID(zoom, x, y);
+}
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tile_cover_impl.hpp b/src/mbgl/util/tile_cover_impl.hpp
new file mode 100644
index 0000000000..7c16718984
--- /dev/null
+++ b/src/mbgl/util/tile_cover_impl.hpp
@@ -0,0 +1,90 @@
+#pragma once
+
+#include <mbgl/util/tile_cover.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <vector>
+#include <map>
+#include <queue>
+
+namespace mbgl {
+
+class TransformState;
+class LatLngBounds;
+
+namespace util {
+
+struct Bound;
+
+using Bounds = std::vector<Bound>;
+using BoundsMap = std::map<uint32_t, Bounds>;
+
+// A chain of points from a local minimum to a local maximum. `winding` indicates
+// the direction of the original geometry.
+struct Bound {
+ std::vector<Point<double>> points;
+ size_t currentPoint = 0;
+ bool winding = false;
+
+ Bound() = default;
+ Bound(const Bound& rhs) {
+ points = rhs.points;
+ currentPoint = rhs.currentPoint;
+ winding = rhs.winding;
+ }
+ Bound& operator=(Bound&& rhs) {
+ points = std::move(rhs.points);
+ currentPoint = rhs.currentPoint;
+ winding = rhs.winding;
+ return *this;
+ }
+
+ // Compute the interpolated x coordinate at y for the current edge
+ double interpolate(uint32_t y) {
+ const auto& p0 = points[currentPoint];
+ const auto& p1 = points[currentPoint + 1];
+
+ const auto dx = p1.x - p0.x;
+ const auto dy = p1.y - p0.y;
+ auto x = p0.x;
+ if (dx == 0) {
+ return x;
+ } else if (dy == 0){
+ return y <= p0.y ? p0.x : p1.x;
+ }
+ if (y < p0.y) return x;
+ if (y > p1.y) return p1.x;
+ x = (dx / dy) * (y - p0.y) + p0.x;
+ return x;
+ }
+};
+
+class TileCover::Impl {
+public:
+ Impl(int32_t z, const Geometry<double>& geom, bool project = true);
+ ~Impl() = default;
+
+ optional<UnwrappedTileID> next();
+ bool hasNext() const;
+
+private:
+ using TileSpans = std::queue<std::pair<int32_t, int32_t>>;
+
+ void nextRow();
+
+ const int32_t zoom;
+ bool isClosed;
+
+ BoundsMap boundsMap;
+ BoundsMap::iterator currentBounds;
+ // List of bounds that begin at or before `tileY`
+ Bounds activeBounds;
+
+ TileSpans tileXSpans;
+ uint32_t tileY;
+ int32_t tileX;
+};
+
+} // namespace util
+} // namespace mbgl
diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp
index 60839357d5..6edcd83bc2 100644
--- a/src/mbgl/util/tiny_sdf.cpp
+++ b/src/mbgl/util/tiny_sdf.cpp
@@ -95,7 +95,7 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do
for (uint32_t i = 0; i < size; i++) {
double distance = gridOuter[i] - gridInner[i];
- sdf.data[i] = std::max(0l, std::min(255l, std::lround(255.0 - 255.0 * (distance / radius + cutoff))));
+ sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff))));
}
return sdf;
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..6cb148a349 100644
--- a/test/api/custom_layer.test.cpp
+++ b/test/api/custom_layer.test.cpp
@@ -34,19 +34,8 @@ void main() {
// layer implementation because it is intended to reflect how someone using custom layers
// might actually write their own implementation.
-class TestLayer {
+class TestLayer : public mbgl::style::CustomLayerHost {
public:
- ~TestLayer() {
- if (program) {
- MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
- 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));
- }
- }
-
void initialize() {
program = MBGL_CHECK_ERROR(glCreateProgram());
vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
@@ -67,7 +56,7 @@ public:
MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 6 * sizeof(GLfloat), triangle, GL_STATIC_DRAW));
}
- void render() {
+ void render(const mbgl::style::CustomLayerRenderParameters&) {
MBGL_CHECK_ERROR(glUseProgram(program));
MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
@@ -75,6 +64,19 @@ public:
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 3));
}
+ void contextLost() {}
+
+ void deinitialize() {
+ if (program) {
+ MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
+ 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;
@@ -90,20 +92,12 @@ 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>(
"custom",
- [] (void* context) {
- reinterpret_cast<TestLayer*>(context)->initialize();
- },
- [] (void* context, const CustomLayerRenderParameters&) {
- reinterpret_cast<TestLayer*>(context)->render();
- },
- [] (void* context) {
- delete reinterpret_cast<TestLayer*>(context);
- }, new TestLayer()));
+ std::make_unique<TestLayer>()));
auto layer = std::make_unique<FillLayer>("landcover", "mapbox");
layer->setSourceLayer("landcover");
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/map/prefetch/expected.png b/test/fixtures/map/prefetch/expected.png
deleted file mode 100644
index e1111b37f7..0000000000
--- a/test/fixtures/map/prefetch/expected.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/map/prefetch/tile_green.png b/test/fixtures/map/prefetch/tile.png
index 553cd10cd1..553cd10cd1 100644
--- a/test/fixtures/map/prefetch/tile_green.png
+++ b/test/fixtures/map/prefetch/tile.png
Binary files differ
diff --git a/test/fixtures/map/prefetch/tile_red.png b/test/fixtures/map/prefetch/tile_red.png
deleted file mode 100644
index 5fa561fb92..0000000000
--- a/test/fixtures/map/prefetch/tile_red.png
+++ /dev/null
Binary files differ
diff --git a/test/fixtures/offline_database/satellite_test.db b/test/fixtures/offline_database/satellite_test.db
new file mode 100644
index 0000000000..95dd8617ff
--- /dev/null
+++ b/test/fixtures/offline_database/satellite_test.db
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..9b28b2e01a 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,18 @@ 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::string bucketLeaderID = "test";
+ 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, bucketLeaderID, std::move(symbolInstances) };
ASSERT_FALSE(bucket.hasIconData());
ASSERT_FALSE(bucket.hasTextData());
ASSERT_FALSE(bucket.hasCollisionBoxData());
@@ -120,6 +137,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 9b7f0b5d58..f95e26fd82 100644
--- a/test/map/map.test.cpp
+++ b/test/map/map.test.cpp
@@ -1,5 +1,6 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/stub_map_observer.hpp>
#include <mbgl/test/fake_file_source.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
@@ -23,45 +24,6 @@ using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::string_literals;
-class StubMapObserver : public MapObserver {
-public:
- void onWillStartLoadingMap() final {
- if (onWillStartLoadingMapCallback) {
- onWillStartLoadingMapCallback();
- }
- }
-
- void onDidFinishLoadingMap() final {
- if (onDidFinishLoadingMapCallback) {
- onDidFinishLoadingMapCallback();
- }
- }
-
- void onDidFailLoadingMap(std::exception_ptr) final {
- if (didFailLoadingMapCallback) {
- didFailLoadingMapCallback();
- }
- }
-
- void onDidFinishLoadingStyle() final {
- if (didFinishLoadingStyleCallback) {
- didFinishLoadingStyleCallback();
- }
- }
-
- void onDidFinishRenderingFrame(RenderMode mode) final {
- if (didFinishRenderingFrame) {
- didFinishRenderingFrame(mode);
- }
- }
-
- std::function<void()> onWillStartLoadingMapCallback;
- std::function<void()> onDidFinishLoadingMapCallback;
- std::function<void()> didFailLoadingMapCallback;
- std::function<void()> didFinishLoadingStyleCallback;
- std::function<void(RenderMode)> didFinishRenderingFrame;
-};
-
template <class FileSource = StubFileSource>
class MapTest {
public:
@@ -72,14 +34,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)
@@ -371,7 +333,7 @@ TEST(Map, MapLoadingSignal) {
MapTest<> test;
bool emitted = false;
- test.observer.onWillStartLoadingMapCallback = [&]() {
+ test.observer.willStartLoadingMapCallback = [&]() {
emitted = true;
};
test.map.getStyle().loadJSON(util::read_file("test/fixtures/api/empty.json"));
@@ -381,7 +343,7 @@ TEST(Map, MapLoadingSignal) {
TEST(Map, MapLoadedSignal) {
MapTest<> test { 1, MapMode::Continuous };
- test.observer.onDidFinishLoadingMapCallback = [&]() {
+ test.observer.didFinishLoadingMapCallback = [&]() {
test.runLoop.stop();
};
@@ -607,7 +569,7 @@ TEST(Map, TEST_DISABLED_ON_CI(ContinuousRendering)) {
HeadlessFrontend frontend(pixelRatio, fileSource, threadPool);
StubMapObserver observer;
- observer.didFinishRenderingFrame = [&] (MapObserver::RenderMode) {
+ observer.didFinishRenderingFrameCallback = [&] (MapObserver::RenderMode) {
// Start a timer that ends the test one second from now. If we are continuing to render
// indefinitely, the timer will be constantly restarted and never trigger. Instead, the
// emergency shutoff above will trigger, failing the test.
diff --git a/test/map/prefetch.test.cpp b/test/map/prefetch.test.cpp
index 5c9a22d1bf..9b61224027 100644
--- a/test/map/prefetch.test.cpp
+++ b/test/map/prefetch.test.cpp
@@ -1,5 +1,6 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/stub_file_source.hpp>
+#include <mbgl/test/stub_map_observer.hpp>
#include <mbgl/map/map.hpp>
#include <mbgl/gl/headless_frontend.hpp>
@@ -17,34 +18,38 @@
using namespace mbgl;
using namespace mbgl::style;
using namespace std::literals::string_literals;
+using namespace std::chrono_literals;
TEST(Map, PrefetchTiles) {
util::RunLoop runLoop;
ThreadPool threadPool(4);
StubFileSource fileSource;
+
+ util::Timer emergencyShutoff;
+ emergencyShutoff.start(10s, 0s, [&] {
+ runLoop.stop();
+ FAIL() << "Did not stop rendering";
+ });
+
+ StubMapObserver observer;
+ observer.didFinishLoadingMapCallback = [&] () {
+ runLoop.stop();
+ };
+
HeadlessFrontend frontend { { 512, 512 }, 1, fileSource, threadPool };
- Map map(frontend, MapObserver::nullObserver(), frontend.getSize(), 1, fileSource, threadPool, MapMode::Still);
+ Map map(frontend, observer, frontend.getSize(), 1, fileSource, threadPool, MapMode::Continuous);
std::vector<int> tiles;
fileSource.response = [&] (const Resource& res) -> optional<Response> {
- Response response;
+ static std::string tile = util::read_file("test/fixtures/map/prefetch/tile.png");
auto zoom = std::stoi(res.url);
tiles.push_back(zoom);
- // Return a red tile for prefetched tiles or green to the actual tile.
- // The end rendering result should be all green because the map is only
- // considered fully rendered when only ideal tiles are shown.
- if (zoom == int(map.getZoom()) + 1) {
- response.data = std::make_shared<std::string>(
- util::read_file("test/fixtures/map/prefetch/tile_green.png"));
- } else {
- response.data = std::make_shared<std::string>(
- util::read_file("test/fixtures/map/prefetch/tile_red.png"));
- }
-
- return { std::move(response) };
+ Response response;
+ response.data = std::make_shared<std::string>(tile);
+ return response;
};
auto checkTilesForZoom = [&](int zoom, const std::vector<int>& expected) {
@@ -53,14 +58,11 @@ TEST(Map, PrefetchTiles) {
// Force tile reloading.
map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/empty.json"));
map.getStyle().loadJSON(util::read_file("test/fixtures/map/prefetch/style.json"));
-
map.setLatLngZoom({ 40.726989, -73.992857 }, zoom); // Manhattan
+ runLoop.run();
- // Should always render the ideal tiles (i.e. a green map)
- test::checkImage("test/fixtures/map/prefetch", frontend.render(map));
-
+ ASSERT_EQ(tiles.size(), expected.size());
ASSERT_TRUE(std::is_permutation(tiles.begin(), tiles.end(), expected.begin()));
- ASSERT_FALSE(tiles.empty());
};
// Check defaults, should be 4.
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/src/mbgl/test/stub_map_observer.hpp b/test/src/mbgl/test/stub_map_observer.hpp
new file mode 100644
index 0000000000..1371577473
--- /dev/null
+++ b/test/src/mbgl/test/stub_map_observer.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <mbgl/map/map_observer.hpp>
+
+#include <functional>
+
+namespace mbgl {
+
+class StubMapObserver : public MapObserver {
+public:
+ void onWillStartLoadingMap() final {
+ if (willStartLoadingMapCallback) {
+ willStartLoadingMapCallback();
+ }
+ }
+
+ void onDidFinishLoadingMap() final {
+ if (didFinishLoadingMapCallback) {
+ didFinishLoadingMapCallback();
+ }
+ }
+
+ void onDidFailLoadingMap(std::exception_ptr) final {
+ if (didFailLoadingMapCallback) {
+ didFailLoadingMapCallback();
+ }
+ }
+
+ void onDidFinishLoadingStyle() final {
+ if (didFinishLoadingStyleCallback) {
+ didFinishLoadingStyleCallback();
+ }
+ }
+
+ void onDidFinishRenderingFrame(RenderMode mode) final {
+ if (didFinishRenderingFrameCallback) {
+ didFinishRenderingFrameCallback(mode);
+ }
+ }
+
+ std::function<void()> willStartLoadingMapCallback;
+ std::function<void()> didFinishLoadingMapCallback;
+ std::function<void()> didFailLoadingMapCallback;
+ std::function<void()> didFinishLoadingStyleCallback;
+ std::function<void(RenderMode)> didFinishRenderingFrameCallback;
+};
+
+
+} // namespace mbgl
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..620e6eaa6d 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,44 +38,9 @@ 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;
-};
+void copyFile(const char* orig, const char* dest) {
+ mbgl::util::write_file(dest, mbgl::util::read_file(orig));
+}
} // namespace
@@ -102,10 +66,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>());
@@ -185,6 +147,36 @@ TEST(OfflineDatabase, PutResource) {
EXPECT_EQ("second", *updateGetResult->data);
}
+TEST(OfflineDatabase, TEST_REQUIRES_WRITE(GetResourceFromOfflineRegion)) {
+ using namespace mbgl;
+
+ createDir("test/fixtures/offline_database");
+ deleteFile("test/fixtures/offline_database/satellite.db");
+ copyFile("test/fixtures/offline_database/satellite_test.db", "test/fixtures/offline_database/satellite.db");
+
+ OfflineDatabase db("test/fixtures/offline_database/satellite.db", mapbox::sqlite::ReadOnly);
+
+ Resource resource = Resource::style("mapbox://styles/mapbox/satellite-v9");
+ ASSERT_TRUE(db.get(resource));
+}
+
+TEST(OfflineDatabase, PutAndGetResource) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+
+ Response response1;
+ response1.data = std::make_shared<std::string>("foobar");
+
+ Resource resource = Resource::style("mapbox://example.com/style");
+
+ db.put(resource, response1);
+
+ auto response2 = db.get(resource);
+
+ ASSERT_EQ(*response1.data, *(*response2).data);
+}
+
TEST(OfflineDatabase, PutTile) {
using namespace mbgl;
@@ -607,40 +599,45 @@ TEST(OfflineDatabase, OfflineMapboxTileCount) {
}
static int databasePageCount(const std::string& path) {
- mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
- mapbox::sqlite::Statement stmt = db.prepare("pragma page_count");
- stmt.run();
- return stmt.get<int>(0);
+ mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly };
+ mapbox::sqlite::Statement stmt{ db, "pragma page_count" };
+ mapbox::sqlite::Query query{ stmt };
+ query.run();
+ return query.get<int>(0);
}
static int databaseUserVersion(const std::string& path) {
- mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
- mapbox::sqlite::Statement stmt = db.prepare("pragma user_version");
- stmt.run();
- return stmt.get<int>(0);
+ mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly };
+ mapbox::sqlite::Statement stmt{ db, "pragma user_version" };
+ mapbox::sqlite::Query query{ stmt };
+ query.run();
+ return query.get<int>(0);
}
static std::string databaseJournalMode(const std::string& path) {
- mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
- mapbox::sqlite::Statement stmt = db.prepare("pragma journal_mode");
- stmt.run();
- return stmt.get<std::string>(0);
+ mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly };
+ mapbox::sqlite::Statement stmt{ db, "pragma journal_mode" };
+ mapbox::sqlite::Query query{ stmt };
+ query.run();
+ return query.get<std::string>(0);
}
static int databaseSyncMode(const std::string& path) {
- mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
- mapbox::sqlite::Statement stmt = db.prepare("pragma synchronous");
- stmt.run();
- return stmt.get<int>(0);
+ mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly };
+ mapbox::sqlite::Statement stmt{ db, "pragma synchronous" };
+ mapbox::sqlite::Query query{ stmt };
+ query.run();
+ return query.get<int>(0);
}
static std::vector<std::string> databaseTableColumns(const std::string& path, const std::string& name) {
- mapbox::sqlite::Database db(path, mapbox::sqlite::ReadOnly);
+ mapbox::sqlite::Database db{ path, mapbox::sqlite::ReadOnly };
const auto sql = std::string("pragma table_info(") + name + ")";
- mapbox::sqlite::Statement stmt = db.prepare(sql.c_str());
+ mapbox::sqlite::Statement stmt{ db, sql.c_str() };
+ mapbox::sqlite::Query query{ stmt };
std::vector<std::string> columns;
- while (stmt.run()) {
- columns.push_back(stmt.get<std::string>(1));
+ while (query.run()) {
+ columns.push_back(query.get<std::string>(1));
}
return columns;
}
diff --git a/test/storage/sqlite.test.cpp b/test/storage/sqlite.test.cpp
index 36715a2fd0..918200181f 100644
--- a/test/storage/sqlite.test.cpp
+++ b/test/storage/sqlite.test.cpp
@@ -9,21 +9,23 @@ TEST(SQLite, Statement) {
mapbox::sqlite::Database db(":memory:", mapbox::sqlite::Create | mapbox::sqlite::ReadWrite);
db.exec("CREATE TABLE test (id INTEGER);");
- mapbox::sqlite::Statement stmt1 = db.prepare("INSERT INTO test (id) VALUES (?1);");
- ASSERT_EQ(stmt1.lastInsertRowId(), 0);
- ASSERT_EQ(stmt1.changes(), 0u);
- stmt1.bind(1, 10);
- stmt1.run();
- ASSERT_EQ(stmt1.lastInsertRowId(), 1);
- ASSERT_EQ(stmt1.changes(), 1u);
+ mapbox::sqlite::Statement stmt1{ db, "INSERT INTO test (id) VALUES (?1);" };
+ mapbox::sqlite::Query query1{ stmt1 };
+ ASSERT_EQ(query1.lastInsertRowId(), 0);
+ ASSERT_EQ(query1.changes(), 0u);
+ query1.bind(1, 10);
+ query1.run();
+ ASSERT_EQ(query1.lastInsertRowId(), 1);
+ ASSERT_EQ(query1.changes(), 1u);
- mapbox::sqlite::Statement stmt2 = db.prepare("INSERT INTO test (id) VALUES (?1);");
- ASSERT_EQ(stmt2.lastInsertRowId(), 0);
- ASSERT_EQ(stmt2.changes(), 0u);
- stmt2.bind(1, 20);
- stmt2.run();
- ASSERT_EQ(stmt2.lastInsertRowId(), 2);
- ASSERT_EQ(stmt2.changes(), 1u);
+ mapbox::sqlite::Statement stmt2{ db, "INSERT INTO test (id) VALUES (?1);" };
+ mapbox::sqlite::Query query2{ stmt2 };
+ ASSERT_EQ(query2.lastInsertRowId(), 0);
+ ASSERT_EQ(query2.changes(), 0u);
+ query2.bind(1, 20);
+ query2.run();
+ ASSERT_EQ(query2.lastInsertRowId(), 2);
+ ASSERT_EQ(query2.changes(), 1u);
}
TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) {
@@ -33,6 +35,6 @@ TEST(SQLite, TEST_REQUIRES_WRITE(CantOpenException)) {
mapbox::sqlite::Database("test/fixtures/offline_database/foobar123.db", mapbox::sqlite::ReadOnly);
FAIL();
} catch (mapbox::sqlite::Exception& ex) {
- ASSERT_EQ(ex.code, mapbox::sqlite::Exception::Code::CANTOPEN);
+ ASSERT_EQ(ex.code, mapbox::sqlite::ResultCode::CantOpen);
}
}
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/conversion/stringify.test.cpp b/test/style/conversion/stringify.test.cpp
index 0b2940a0e0..136f276aaf 100644
--- a/test/style/conversion/stringify.test.cpp
+++ b/test/style/conversion/stringify.test.cpp
@@ -80,23 +80,25 @@ TEST(Stringify, Filter) {
}
TEST(Stringify, CameraFunction) {
+ ASSERT_EQ(stringify(CameraFunction<float>(ExponentialStops<float> { {{0, 1}}, 1 })),
+ "[\"interpolate\",[\"linear\"],[\"zoom\"],0.0,1.0]");
ASSERT_EQ(stringify(CameraFunction<float>(ExponentialStops<float> { {{0, 1}}, 2 })),
- "{\"type\":\"exponential\",\"base\":2.0,\"stops\":[[0.0,1.0]]}");
+ "[\"interpolate\",[\"exponential\",2.0],[\"zoom\"],0.0,1.0]");
ASSERT_EQ(stringify(CameraFunction<float>(IntervalStops<float> { {{0, 1}} })),
- "{\"type\":\"interval\",\"stops\":[[0.0,1.0]]}");
+ "[\"step\",[\"zoom\"],0.0,1.0]");
}
TEST(Stringify, SourceFunction) {
ASSERT_EQ(stringify(SourceFunction<float>("property", ExponentialStops<float> { {{0, 1}}, 2 })),
- "{\"property\":\"property\",\"type\":\"exponential\",\"base\":2.0,\"stops\":[[0.0,1.0]]}");
+ "[\"interpolate\",[\"exponential\",2.0],[\"number\",[\"get\",\"property\"]],0.0,1.0]");
ASSERT_EQ(stringify(SourceFunction<float>("property", IntervalStops<float> { {{0, 1}} })),
- "{\"property\":\"property\",\"type\":\"interval\",\"stops\":[[0.0,1.0]]}");
+ "[\"step\",[\"number\",[\"get\",\"property\"]],0.0,1.0]");
ASSERT_EQ(stringify(SourceFunction<float>("property", CategoricalStops<float> { {{CategoricalValue(true), 1}} })),
- "{\"property\":\"property\",\"type\":\"categorical\",\"stops\":[[true,1.0]]}");
+ "[\"case\",[\"boolean\",[\"get\",\"property\"]],1.0,[\"error\"]]");
ASSERT_EQ(stringify(SourceFunction<float>("property", IdentityStops<float> {})),
- "{\"property\":\"property\",\"type\":\"identity\"}");
+ "[\"number\",[\"get\",\"property\"]]");
ASSERT_EQ(stringify(SourceFunction<float>("property", IdentityStops<float> {}, 0.0f)),
- "{\"property\":\"property\",\"type\":\"identity\",\"default\":0.0}");
+ "[\"number\",[\"get\",\"property\"]]");
}
TEST(Stringify, CompositeFunction) {
@@ -108,16 +110,17 @@ TEST(Stringify, CompositeFunction) {
},
2
}, 0.0f)),
- "{\"property\":\"property\",\"type\":\"exponential\",\"base\":2.0,"
- "\"stops\":["
- "[{\"zoom\":0.0,\"value\":0.0},1.0],"
- "[{\"zoom\":1.0,\"value\":0.0},1.0]],\"default\":0.0}");
+ "[\"interpolate\","
+ "[\"linear\"],"
+ "[\"zoom\"],"
+ "0.0,[\"interpolate\",[\"exponential\",2.0],[\"number\",[\"get\",\"property\"]],0.0,1.0],"
+ "1.0,[\"interpolate\",[\"exponential\",2.0],[\"number\",[\"get\",\"property\"]],0.0,1.0]]");
}
TEST(Stringify, PropertyValue) {
ASSERT_EQ(stringify(PropertyValue<float>(1)), "1.0");
ASSERT_EQ(stringify(PropertyValue<float>(CameraFunction<float>(ExponentialStops<float> { {{0, 1}}, 2 }))),
- "{\"type\":\"exponential\",\"base\":2.0,\"stops\":[[0.0,1.0]]}");
+ "[\"interpolate\",[\"exponential\",2.0],[\"zoom\"],0.0,1.0]");
}
TEST(Stringify, Layout) {
diff --git a/test/style/expression/expression.test.cpp b/test/style/expression/expression.test.cpp
new file mode 100644
index 0000000000..9d5e172888
--- /dev/null
+++ b/test/style/expression/expression.test.cpp
@@ -0,0 +1,95 @@
+#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() };
+ if (name == "collator" || name == "line-progress" || name == "resolved-locale") {
+ // Not yet implemented
+ continue;
+ }
+ 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.parseExpression(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..49edcaef45 100644
--- a/test/style/filter.test.cpp
+++ b/test/style/filter.test.cpp
@@ -1,128 +1,121 @@
#include <mbgl/test/util.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/geometry.hpp>
+#include <mbgl/test/stub_geometry_tile_feature.hpp>
#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);
+bool filter(const char * json,
+ const PropertyMap& featureProperties = {{}},
+ optional<FeatureIdentifier> featureId = {},
+ FeatureType featureType = FeatureType::Point,
+ GeometryCollection featureGeometry = {},
+ float zoom = 0.0f) {
conversion::Error error;
- optional<Filter> filter = conversion::convert<Filter, JSValue>(doc, error);
+ optional<Filter> filter = conversion::convertJSON<Filter>(json, error);
EXPECT_TRUE(bool(filter));
- return *filter;
-}
-
-Feature feature(const PropertyMap& properties, const Geometry<double>& geometry = Point<double>()) {
- Feature result { geometry };
- result.properties = properties;
- return result;
+ EXPECT_EQ(error.message, "");
+
+ StubGeometryTileFeature feature { featureId, featureType, featureGeometry, featureProperties };
+ expression::EvaluationContext context = { zoom, &feature };
+
+ return (*filter)(context);
}
TEST(Filter, EqualsString) {
- Filter f = parse(R"(["==", "foo", "bar"])");
- ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }})));
- ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }})));
+ auto f = R"(["==", "foo", "bar"])";
+ ASSERT_TRUE(filter(f, {{ "foo", std::string("bar") }}));
+ ASSERT_FALSE(filter(f, {{ "foo", std::string("baz") }}));
}
TEST(Filter, EqualsNumber) {
- Filter f = parse(R"(["==", "foo", 0])");
- ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }})));
- ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }})));
- ASSERT_TRUE(f(feature({{ "foo", double(0) }})));
- ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }})));
- ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }})));
- ASSERT_FALSE(f(feature({{ "foo", double(1) }})));
- ASSERT_FALSE(f(feature({{ "foo", std::string("0") }})));
- ASSERT_FALSE(f(feature({{ "foo", false }})));
- ASSERT_FALSE(f(feature({{ "foo", true }})));
- ASSERT_FALSE(f(feature({{ "foo", mapbox::geometry::null_value }})));
- ASSERT_FALSE(f(feature({{}})));
+ auto f = R"(["==", "foo", 0])";
+ ASSERT_TRUE(filter(f, {{ "foo", int64_t(0) }}));
+ ASSERT_TRUE(filter(f, {{ "foo", uint64_t(0) }}));
+ ASSERT_TRUE(filter(f, {{ "foo", double(0) }}));
+ ASSERT_FALSE(filter(f, {{ "foo", int64_t(1) }}));
+ ASSERT_FALSE(filter(f, {{ "foo", uint64_t(1) }}));
+ ASSERT_FALSE(filter(f, {{ "foo", double(1) }}));
+ ASSERT_FALSE(filter(f, {{ "foo", std::string("0") }}));
+ ASSERT_FALSE(filter(f, {{ "foo", false }}));
+ ASSERT_FALSE(filter(f, {{ "foo", true }}));
+ ASSERT_FALSE(filter(f, {{ "foo", mapbox::geometry::null_value }}));
+ ASSERT_FALSE(filter(f, {{}}));
}
TEST(Filter, EqualsType) {
- Filter f = parse(R"(["==", "$type", "LineString"])");
- ASSERT_FALSE(f(feature({{}}, Point<double>())));
- ASSERT_TRUE(f(feature({{}}, LineString<double>())));
+ auto f = R"(["==", "$type", "LineString"])";
+ ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point, {}));
+ ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString, {}));
}
TEST(Filter, InType) {
- Filter f = parse(R"(["in", "$type", "LineString", "Polygon"])");
- ASSERT_FALSE(f(feature({{}}, Point<double>())));
- ASSERT_TRUE(f(feature({{}}, LineString<double>())));
- ASSERT_TRUE(f(feature({{}}, Polygon<double>())));
+ auto f = R"(["in", "$type", "LineString", "Polygon"])";
+ ASSERT_FALSE(filter(f, {{}}, {}, FeatureType::Point));
+ ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::LineString));
+ ASSERT_TRUE(filter(f, {{}}, {}, FeatureType::Polygon));
}
TEST(Filter, Any) {
- ASSERT_FALSE(parse("[\"any\"]")(feature({{}})));
- ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_FALSE(filter("[\"any\"]"));
+ ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(filter("[\"any\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(filter("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
}
TEST(Filter, All) {
- ASSERT_TRUE(parse("[\"all\"]")(feature({{}})));
- ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_TRUE(filter("[\"all\"]", {{}}));
+ ASSERT_TRUE(filter("[\"all\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(filter("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
}
TEST(Filter, None) {
- ASSERT_TRUE(parse("[\"none\"]")(feature({{}})));
- ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
- feature({{ std::string("foo"), int64_t(1) }})));
+ ASSERT_TRUE(filter("[\"none\"]"));
+ ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(filter("[\"none\", [\"==\", \"foo\", 0]]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(filter("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]", {{ std::string("foo"), int64_t(1) }}));
}
TEST(Filter, Has) {
- ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
- feature({{ std::string("foo"), int64_t(0) }})));
- ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
- feature({{ std::string("foo"), false }})));
- ASSERT_FALSE(parse("[\"has\", \"foo\"]")(
- feature({{}})));
+ ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }}));
+ ASSERT_TRUE(filter("[\"has\", \"foo\"]", {{ std::string("foo"), false }}));
+ ASSERT_FALSE(filter("[\"has\", \"foo\"]"));
}
TEST(Filter, NotHas) {
- ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
- feature({{ std::string("foo"), int64_t(1) }})));
- ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
- feature({{ std::string("foo"), int64_t(0) }})));
- ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
- feature({{ std::string("foo"), false }})));
- ASSERT_TRUE(parse("[\"!has\", \"foo\"]")(
- feature({{}})));
+ ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), int64_t(0) }}));
+ ASSERT_FALSE(filter("[\"!has\", \"foo\"]", {{ std::string("foo"), false }}));
+ ASSERT_TRUE(filter("[\"!has\", \"foo\"]"));
}
TEST(Filter, ID) {
- Feature feature1 { Point<double>() };
- feature1.id = { uint64_t(1234) };
+ FeatureIdentifier id1 { uint64_t{ 1234 } };
+ ASSERT_TRUE(filter("[\"==\", \"$id\", 1234]", {{}}, id1));
+ ASSERT_FALSE(filter("[\"==\", \"$id\", \"1234\"]", {{}}, id1));
+
+ ASSERT_FALSE(filter("[\"==\", \"$id\", 1234]", {{ "id", uint64_t(1234) }}));
+}
- ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature1));
- ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature1));
+TEST(Filter, Expression) {
+ ASSERT_TRUE(filter("[\"==\", [\"+\", 1, 1], 2]"));
+ ASSERT_FALSE(filter("[\"==\", [\"+\", 1, 1], 4]"));
+}
- Feature feature2 { Point<double>() };
- feature2.properties["id"] = { uint64_t(1234) };
+TEST(Filter, PropertyExpression) {
+ ASSERT_TRUE(filter("[\"==\", [\"get\", \"two\"], 2]", {{"two", int64_t(2)}}));
+ ASSERT_FALSE(filter("[\"==\", [\"get\", \"two\"], 4]", {{"two", int64_t(2)}}));
+}
- ASSERT_FALSE(parse("[\"==\", \"$id\", 1234]")(feature2));
+TEST(Filter, ZoomExpressionNested) {
+ ASSERT_TRUE(filter(R"(["==", ["get", "two"], ["zoom"]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f));
+ ASSERT_FALSE(filter(R"(["==", ["get", "two"], ["+", ["zoom"], 1]])", {{"two", int64_t(2)}}, {}, FeatureType::Point, {}, 2.0f));
}
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..5d255a6105
--- /dev/null
+++ b/test/text/cross_tile_symbol_index.test.cpp
@@ -0,0 +1,207 @@
+#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;
+ std::string bucketLeaderID = "test";
+
+ 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, bucketLeaderID, 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, bucketLeaderID, 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, bucketLeaderID, 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, bucketLeaderID, 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;
+ std::string bucketLeaderID = "test";
+
+ 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, bucketLeaderID, 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, bucketLeaderID, 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;
+ std::string bucketLeaderID = "test";
+
+ 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, bucketLeaderID, 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, bucketLeaderID, 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;
+ std::string bucketLeaderID = "test";
+
+ 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, bucketLeaderID, 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, bucketLeaderID, 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..cd53c7c4a4 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>
@@ -66,39 +65,6 @@ TEST(VectorTile, onError) {
EXPECT_TRUE(tile.isComplete());
}
-TEST(VectorTile, Issue7615) {
- VectorTileTest test;
- VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
-
- style::SymbolLayer symbolLayer("symbol", "source");
- 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);
-
- // Simulate placement of a symbol layer.
- tile.onPlacement(GeometryTile::PlacementResult {
- {{
- symbolLayer.getID(),
- symbolBucket
- }},
- nullptr,
- {},
- {},
- }, 0);
-
- // Subsequent onLayout should not cause the existing symbol bucket to be discarded.
- tile.onLayout(GeometryTile::LayoutResult {
- std::unordered_map<std::string, std::shared_ptr<Bucket>>(),
- nullptr,
- nullptr,
- }, 0);
-
- EXPECT_EQ(symbolBucket.get(), tile.getBucket(*symbolLayer.baseImpl));
-}
-
TEST(VectorTile, Issue8542) {
VectorTileTest test;
VectorTile tile(OverscaledTileID(0, 0, 0), "source", test.tileParameters, test.tileset);
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/tile_cover.test.cpp b/test/util/tile_cover.test.cpp
index 933c18b5ea..7defa761af 100644
--- a/test/util/tile_cover.test.cpp
+++ b/test/util/tile_cover.test.cpp
@@ -2,6 +2,8 @@
#include <mbgl/util/geo.hpp>
#include <mbgl/map/transform.hpp>
+#include <algorithm>
+
#include <gtest/gtest.h>
using namespace mbgl;
@@ -22,8 +24,8 @@ TEST(TileCover, Antarctic) {
TEST(TileCover, WorldZ0) {
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 0, 0, 0 },
- }),
+ { 0, 0, 0 },
+ }),
util::tileCover(LatLngBounds::world(), 0));
}
@@ -37,15 +39,15 @@ TEST(TileCover, Pitch) {
transform.setPitch(40.0 * M_PI / 180.0);
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 }
- }),
+ { 2, 1, 2 }, { 2, 1, 1 }, { 2, 2, 2 }, { 2, 2, 1 }, { 2, 3, 2 }
+ }),
util::tileCover(transform.getState(), 2));
}
TEST(TileCover, WorldZ1) {
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 },
- }),
+ { 1, 0, 0 }, { 1, 0, 1 }, { 1, 1, 0 }, { 1, 1, 1 },
+ }),
util::tileCover(LatLngBounds::world(), 1));
}
@@ -59,21 +61,44 @@ TEST(TileCover, SingletonZ1) {
util::tileCover(LatLngBounds::singleton({ 0, 0 }), 1));
}
+TEST(TileCoverStream, Arctic) {
+ auto bounds = LatLngBounds::hull({ 84, -180 }, { 70, 180 });
+ auto zoom = 3;
+ util::TileCover tc(bounds, zoom);
+ auto results = util::tileCover(bounds, zoom);
+ std::vector<UnwrappedTileID> t;
+ while(tc.hasNext()) {
+ t.push_back(*tc.next());
+ };
+ EXPECT_EQ(t.size(), results.size());
+}
+
+TEST(TileCoverStream, WorldZ1) {
+ auto zoom = 1;
+ util::TileCover tc(LatLngBounds::world(), zoom);
+ std::vector<UnwrappedTileID> t;
+ while(tc.hasNext()) {
+ t.push_back(*tc.next());
+ };
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ { 1, 0, 0 }, { 1, 1, 0 }, { 1, 0, 1 }, { 1, 1, 1 },
+ }), t);
+}
+
static const LatLngBounds sanFrancisco =
LatLngBounds::hull({ 37.6609, -122.5744 }, { 37.8271, -122.3204 });
TEST(TileCover, SanFranciscoZ0) {
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 0, 0, 0 },
- }),
+ { 0, 0, 0 },
+ }),
util::tileCover(sanFrancisco, 0));
}
TEST(TileCover, SanFranciscoZ10) {
EXPECT_EQ((std::vector<UnwrappedTileID>{
- { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 },
-
- }),
+ { 10, 163, 395 }, { 10, 163, 396 }, { 10, 164, 395 }, { 10, 164, 396 },
+ }),
util::tileCover(sanFrancisco, 10));
}
@@ -85,11 +110,192 @@ TEST(TileCover, SanFranciscoZ0Wrapped) {
util::tileCover(sanFranciscoWrapped, 0));
}
+TEST(TileCover, GeomPoint) {
+ auto point = Point<double>{ -122.5744, 37.6609 };
+
+ EXPECT_EQ((std::vector<UnwrappedTileID>{ {2 ,0 ,1 } }),
+ util::tileCover(point, 2));
+}
+
+TEST(TileCover, GeomMultiPoint) {
+ auto points = MultiPoint<double>{ { -122.5, 37.76 }, { -122.4, 37.76} };
+
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ {20, 167480, 405351}, {20, 167772, 405351} }),
+ util::tileCover(points, 20));
+}
+
+TEST(TileCover, GeomLineZ10) {
+ auto lineCover = util::tileCover(LineString<double>{
+ {-121.49368286132812,38.57903714667459},
+ {-122.4422836303711,37.773157169570695}
+ }, 10);
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ { 10, 166, 392}, {10, 165, 393}, {10, 166, 393},
+ {10, 164, 394}, {10, 165, 394}, {10,163,395}, {10, 164, 395}
+ }),lineCover);
+
+}
+
+TEST(TileCover, WrappedGeomLineZ10) {
+ auto lineCover = util::tileCover(LineString<double>{
+ {-179.93342914581299,38.892101707724315},
+ {-180.02394485473633,38.89203490311832}
+ }, 10);
+ EXPECT_EQ((std::vector<UnwrappedTileID>{ { 10, -1, 391 }, { 10, 0, 391 } }),
+ lineCover);
+
+ lineCover = util::tileCover(LineString<double>{
+ {179.93342914581299,38.892101707724315},
+ {180.02394485473633,38.89203490311832}
+ }, 10);
+ EXPECT_EQ((std::vector<UnwrappedTileID>{ { 10, 1023, 391 }, { 10, 1024, 391 } }),
+ lineCover);
+}
+
+TEST(TileCover, GeomMultiLineString) {
+ auto geom = MultiLineString<double>{
+ { { -122.5, 37.76 }, { -122.4, 37.76} },
+ { { -122.5, 37.72 }, { -122.4, 37.72} } };
+
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ {14, 2616, 6333}, {14, 2617, 6333}, {14, 2618, 6333},
+ {14, 2619, 6333}, {14, 2620, 6333}, {14, 2621, 6333},
+ {14, 2616, 6335}, {14, 2617, 6335}, {14, 2618, 6335},
+ {14, 2619, 6335}, {14, 2620, 6335}, {14, 2621, 6335}}),
+ util::tileCover(geom, 14));
+}
+
+TEST(TileCover, GeomPolygon) {
+ auto polygon = Polygon<double>{
+ {
+ {5.09765625,53.067626642387374},
+ {2.373046875,43.389081939117496},
+ {-4.74609375,48.45835188280866},
+ {-1.494140625,37.09023980307208},
+ {22.587890625,36.24427318493909},
+ {31.640625,46.13417004624326},
+ {17.841796875,54.7246201949245},
+ {5.09765625,53.067626642387374},
+ },{
+ {19.6875,49.66762782262194},
+ {22.8515625,43.51668853502906},
+ {13.623046875,45.089035564831036},
+ {16.34765625,39.095962936305476},
+ {5.185546875,41.244772343082076},
+ {8.701171874999998,50.233151832472245},
+ {19.6875,49.66762782262194}
+ }
+ };
+
+ auto results = util::tileCover(polygon, 8);
+
+ EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 134, 87}), results.end());
+ EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end());
+ // Should have a hole
+ EXPECT_EQ(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end());
+}
+
+TEST(TileCover, GeomMultiPolygon) {
+ auto multiPolygon = MultiPolygon<double>{
+ {{
+ {5.09765625,53.067626642387374},
+ {2.373046875,43.389081939117496},
+ {-4.74609375,48.45835188280866},
+ {-1.494140625,37.09023980307208},
+ {22.587890625,36.24427318493909},
+ {31.640625,46.13417004624326},
+ {17.841796875,54.7246201949245},
+ {5.09765625,53.067626642387374},
+ }},{{
+ {59.150390625,45.460130637921004},
+ {65.126953125,41.11246878918088},
+ {69.169921875,47.45780853075031},
+ {63.896484375,50.064191736659104},
+ {59.150390625,45.460130637921004}
+ }}
+ };
+ auto results = util::tileCover(multiPolygon, 8);
+
+ EXPECT_EQ(424u, results.size());
+ EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 139, 87}), results.end());
+ EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 136, 87}), results.end());
+ EXPECT_NE(std::find(results.begin(), results.end(), UnwrappedTileID{8, 174, 94}), results.end());
+}
+
+TEST(TileCover, GeomSanFranciscoPoly) {
+ auto sanFranciscoGeom = Polygon<double>{
+ {
+ {-122.5143814086914,37.779127216982424},
+ {-122.50811576843262,37.72721239056709},
+ {-122.50313758850099,37.70820178063929},
+ {-122.3938751220703,37.707454835665274},
+ {-122.37567901611328,37.70663997801684},
+ {-122.36297607421874,37.71343018466285},
+ {-122.354736328125,37.727280276860036},
+ {-122.36469268798828,37.73868429065797},
+ {-122.38014221191408,37.75442980295571},
+ {-122.38391876220702,37.78753873820529},
+ {-122.35919952392578,37.8065289741725},
+ {-122.35679626464844,37.820632846207864},
+ {-122.3712158203125,37.835276322922695},
+ {-122.3818588256836,37.82958198283902},
+ {-122.37190246582031,37.80788523279169},
+ {-122.38735198974608,37.791337175930686},
+ {-122.40966796874999,37.812767557570204},
+ {-122.46425628662108,37.807071480609274},
+ {-122.46803283691405,37.810326435534755},
+ {-122.47901916503906,37.81168262440736},
+ {-122.48966217041016,37.78916666399649},
+ {-122.50579833984375,37.78781006166096},
+ {-122.5143814086914,37.779127216982424}
+ }
+ };
+
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ { 12, 654, 1582 }, { 12, 655, 1582 },
+ { 12, 654, 1583 }, { 12, 655, 1583 },
+ { 12, 654, 1584 }, { 12, 655, 1584 }
+ }), util::tileCover(sanFranciscoGeom, 12));
+}
+
+TEST(TileCover, GeomInvalid) {
+ auto point = Point<double>{ -122.5744, 97.6609 };
+ EXPECT_THROW(util::tileCover(point, 2), std::domain_error);
+
+ auto badPoly = Polygon<double> { { {1.0, 35.0} } };
+ EXPECT_EQ((std::vector<UnwrappedTileID>{ }), util::tileCover(badPoly, 16));
+
+ //Should handle open polygons.
+ badPoly = Polygon<double> { { {1.0, 34.2}, {1.0, 34.4}, {0.5, 34.3} } };
+ EXPECT_EQ((std::vector<UnwrappedTileID>{
+ { 10, 513, 407 }, { 10, 514, 407},
+ { 10, 513, 408 }, { 10, 514, 408}
+ }), util::tileCover(badPoly, 10));
+}
+
+
+TEST(TileCount, World) {
+ EXPECT_EQ(1u, util::tileCount(LatLngBounds::world(), 0));
+ EXPECT_EQ(4u, util::tileCount(LatLngBounds::world(), 1));
+}
+
TEST(TileCount, SanFranciscoZ10) {
- EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10, util::tileSize));
+ EXPECT_EQ(4u, util::tileCount(sanFrancisco, 10));
+}
+
+TEST(TileCount, SanFranciscoWrappedZ10) {
+ EXPECT_EQ(4u, util::tileCount(sanFranciscoWrapped, 10));
}
TEST(TileCount, SanFranciscoZ22) {
- EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22, util::tileSize));
+ EXPECT_EQ(7254450u, util::tileCount(sanFrancisco, 22));
}
+TEST(TileCount, BoundsCrossingAntimeridian) {
+ auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830});
+
+ EXPECT_EQ(1u, util::tileCount(crossingBounds, 0));
+ EXPECT_EQ(4u, util::tileCount(crossingBounds, 3));
+ EXPECT_EQ(8u, util::tileCount(crossingBounds, 4));
+}
diff --git a/test/util/tile_range.test.cpp b/test/util/tile_range.test.cpp
index c4c37c74d7..83ac5096a5 100644
--- a/test/util/tile_range.test.cpp
+++ b/test/util/tile_range.test.cpp
@@ -52,14 +52,14 @@ TEST(TileRange, ContainsWrappedBounds) {
TEST(TileRange, ContainsBoundsCrossingAntimeridian) {
{
- auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830});
- auto range = util::TileRange::fromLatLngBounds(cossingBounds, 1);
+ auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830});
+ auto range = util::TileRange::fromLatLngBounds(crossingBounds, 1);
EXPECT_TRUE(range.contains(CanonicalTileID(1, 1, 1)));
EXPECT_TRUE(range.contains(CanonicalTileID(1, 0, 0)));
}
{
- auto cossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830});
- auto range = util::TileRange::fromLatLngBounds(cossingBounds, 6);
+ auto crossingBounds = LatLngBounds::hull({-20.9615, -214.309}, {19.477, -155.830});
+ auto range = util::TileRange::fromLatLngBounds(crossingBounds, 6);
EXPECT_FALSE(range.contains(CanonicalTileID(6, 55, 34)));
EXPECT_FALSE(range.contains(CanonicalTileID(6, 5, 28)));
EXPECT_TRUE(range.contains(CanonicalTileID(6, 63, 28)));
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);
+}